oscar@utcsrgv.UUCP (Oscar M. Nierstrasz) (05/12/84)
echo README sed 's/^|//' > README << 'Rosebud' |Here are a few shell scripts and a C program for dealing with future |events. `dates' is a program similar to `calendar' except that you can |ask it to report future events as well as today's. I find it useful |for birthdays, seminars, and anything that needs reminding more than a |day in advance. See `dates.1'. I've been using the program for quite |a while now, and believe it to be stable (recently caught a leap-year bug). | |`events' is a shell script that calls `dates' on a single, publicly |accessible file. A flag lets you edit it while temporarily locking out |other attempts to do so. I use it for keeping track of upcoming films, |but it clearly has other applications. | |`remind' is a shell script that does more-or-less what `leave' does. |The difference is that it won't go away if you log out, and it will |contact you even if you change terminals. It calls the enclosed shell |script `hi' to do this. | |Note: fix paths in `events' and `remind'. | |Enjoy. | Oscar Nierstrasz | |{ akgua allegra decvax decwrl floyd ihnp4 linus ... }!utcsrgv!oscar Rosebud chmod 0644 README echo dates.1 sed 's/^|//' > dates.1 << 'Rosebud' |.TH DATES 1 |.UC |.SH NAME |dates \- report upcoming events |.SH SYNOPSIS |.B dates |[ -t days ] file ... |.SH DESCRIPTION |.I Dates |examines a file containing information about upcoming events |and reports any that are to take place within the next seven days. |.PP |The event file is expected to contain entries of the form: |.IP |mmdd |.br |event information |.br | ... |.PP |if that event takes place on a single day, or: |.IP |mmdd-mmdd |.br |event information |.br | ... |.PP |if it takes place over a range of dates. |Comment lines beginning with a '#' are ignored. |.PP |For example, if your event file contains an entry for: |.IP |1206 |.in +5 |Saint Nicholas' Day! |.PP |then |.IP |dates eventfile |.PP |will yield: |.IP |Tue Dec 6: |.in +5 |Saint Nicholas' Day! |.PP |assuming, of course, that it falls within seven days. |Events occurring the same day or the next are reported as |taking place "Today" or "Tomorrow". |.PP |You can shorten or lengthen the time period for which events are |reported by using the '-t' flag. |For example: |.IP |dates -t 0 eventfile |.PP |will report only those events taking place today. |.PP |Event entries may be listed in any order, but be warned that |.I dates |does not sort its output. |Upcoming events are reported in the order that they appear. |Obsolete entries are not deleted by |.I dates |since it has no way of telling that an event will not recur |next year (like birthdays). |.PP |It is often useful to put a call to |.I dates |in your .login or .logout (with the "C" shell) or your .profile |(with the Bourne shell). |.SH BUGS |Doesn't seem to work quite right at midnight or New Year's. |.SH AUTHOR |Oscar Nierstrasz at the University of Toronto. |.SH SEE ALSO |events(1), calendar(1), at(1), leave(1), time(2). Rosebud chmod 0644 dates.1 echo dates.c sed 's/^|//' > dates.c << 'Rosebud' | |/* FILE: DATES.C Jan. 1983 | * | * AUTHOR: OSCAR NIERSTRASZ | * | * Usage: dates [-t <days>] <file> ... | * | * Checks files of events and warns you if they | * are coming up soon. | * Flags and files may appear in any order. | * | * -t sets the number of days | * | * Assumes files have either structure: | * mmdd | * event | * ... | * mmdd-mmdd | * event | * ... | */ | |#include <stdio.h> |#include <sgtty.h> |#include <sys/types.h> |#include <sys/timeb.h> |#define TRUE 1 |#define FALSE 0 |#define MAX 500 | |#define EOS '\0' |#define RANGE '-' |#define COMMENT '#' | |#define DLEN 30 /* date buffer length */ | |#define NEXT 1 /* NEXT: use next year's date if this */ |#define THIS 0 /* year's has passed; THIS: don't */ | |#define TDIF 5 /* 5 hours earlier than Greenwich */ |#define DAYS 7 /* default warning period */ |#define num(n) (n - '0') |#define dysize(yr) (365 + (((yr%4)==0)?1:0)) |#define dmsize(yr, m) (dm[m] + (((yr % 4) == 0) && m >= 2)) |#define month_name(n) ((n < 1 || n > 12) ? name[0] : name[n]) |#define day_name(n) weekday[((n)%7+7)%7] | |long *time(); |int days = DAYS, putline = FALSE; | |struct ymdhms{ | int year; | int month; | int day; | int hour; | int min; | int sec; | int wday; |} t; /* Today's date */ | |static int dm[13] = |{ | 0, | 31, | 59, | 90, | 120, | 151, | 181, | 212, | 243, | 273, | 304, | 334, | 365 |}; | |static char *name[] = { | "illegal month", | "Jan", | "Feb", | "March", | "April", | "May", | "June", | "July", | "Aug", | "Sept", | "Oct", | "Nov", | "Dec" | }; | |static char *weekday[] = { | "Wed", | "Thu", | "Fri", | "Sat", | "Sun", | "Mon", | "Tue" | }; | |char *nom(s) |char *s; |{ | char *c; | | c = s; | while(*c != EOS){ | if(*c == ','){ | *c = EOS; | c++; | while(*c == ' ') | c++; | return(c); | } | c++; | } | return(c); |} | |main(argc, argv) |int argc; |char *argv[]; |{ | FILE *fp; | int i=1, ll; | char s[MAX]; | | if(argc == 1){ | fprintf(stderr, "Usage: dates [-t <days>] <file> ...\n"); | exit(1); | } | rtime(); | while(i<argc){ | if(argv[i][0] == '-'){ | switch(argv[i][1]){ | case 't': | i++; | if(i == argc){ | fprintf(stderr, "dates: missing arg\n"); | } | else days = atoi(argv[i]); | break; | default: | fprintf(stderr, "dates: unknown flag %s\n", argv[i]); | } | } | else{ | if((fp = fopen(argv[i], "r")) == NULL) | fprintf(stderr, "dates: can't open %s\n", argv[i]); | else{ | while((ll = getline(s, fp)) != EOF){ | if(ll == MAX) | fprintf(stderr, "dates: line too long: %s\n", s); | if(s[0] != COMMENT) | if(data(s)){ | if (putline) | printf("%s\n", s); | } | else tcheck(s); | } | fclose(fp); | } | } | i++; | } |} | |rtime() |{ |/* sets today's date in t. */ | | int i, days; | long tbuf; | | time(&tbuf); /* seconds */ | t.sec = tbuf % 60; | tbuf = tbuf / 60; /* minutes */ | t.min = tbuf % 60; | tbuf = tbuf / 60 - TDIF; /* hours */ | t.hour = tbuf % 24; | tbuf = 1 + tbuf / 24; /* days */ | t.wday = tbuf % 7; | t.year = 1970; | days = dysize(t.year); | while(tbuf > days){ | (t.year)++; | tbuf -= days; | days = dysize(t.year); | } | for(i=1; i<=12; i++){ | days = dmsize(t.year, i); | if(tbuf < days) | break; | } | t.month = i; | t.day = tbuf - dmsize(t.year, i-1); |} | |tcheck(s) |char *s; |{ | char *sn, *f, date1[DLEN], date2[DLEN]; | int dif1, dif2; | | getday(s, &dif1, date1, NEXT); | if(s[4] == EOS){ | if(dif1 <= days){ | putline = TRUE; | printf("%s :\n", date1); | } | else putline = FALSE; | } | else if(s[4] == RANGE){ | getday(s+5, &dif2, date2, NEXT); | /* if date1 has passed but date2 hasn't, */ | /* force getday to use this year for date1 */ | if(dif2 < dif1) | getday(s, &dif1, date1, THIS); | if((dif1 <= days) && (dif2 >= 0)){ | putline = TRUE; | printf("%s to %s :\n", date1, date2); | } | else putline = FALSE; | } |} | |getday(s, pdif, date, next) |char *s, *date; |int *pdif, next; |{ | int m, d, dif; | | m = num(s[0])*10 + num(s[1]); | d = num(s[2])*10 + num(s[3]); | *pdif = dif = dmsize(t.year, m-1) + d | - (dmsize(t.year, t.month-1) + t.day); | if(next && dif < 0) | *pdif = dif = dysize(1+t.year) + dmsize(1+t.year, m) | + d - (dmsize(t.year, t.month) + t.day); | if(dif == -1) | sprintf(date, "Yesterday"); | else if(dif == 0) | sprintf(date, "Today"); | else if(dif == 1) | sprintf(date, "Tomorrow"); | else | sprintf(date, "%s %s %d", day_name(dif+t.wday), | month_name(m), d); |} |data(s) |char *s; |{ | /* Returns TRUE if line is data */ | /* FALSE if date line */ | /* Acceptable formats are "mmdd" */ | /* and "mmdd-mmdd" */ | int i; | | for(i=0; i<4; i++) | if(s[i] < '0' || '9' < s[i]) | return(TRUE); | if(s[4] == EOS) | return(FALSE); | /* date may be a range */ | if(s[4] == RANGE){ | /* Can't have "nested" ranges */ | if(s[9] != EOS) | return(TRUE); | return(data(s+5)); | } | return(TRUE); |} |getline(s,file) | char *s; | FILE *file; |{ | /* get a line of input up to MAX characters */ | /* return length of input or EOF or MAX */ | int i=0; | char c; | | while((c=fgetc(file)) != '\n'){ | if(c == EOF){ | rewind(file); | return(EOF); | } | s[i] = c; | i++; | if(i == MAX){ | s[i-1] = '\0'; | return(MAX); | } | } | s[i] = '\0'; | return(i); |} Rosebud chmod 0644 dates.c echo events sed 's/^|//' > events << 'Rosebud' |#! /bin/sh |# events [N]|-[vhe]|[-em]|[-rm] |# List today's events or events for the next N days. |# With the -v, -h, -e or -em flags, vi, hed, ed or emacs |# are invoked on the database. |# The -rm flag removes the lock file. |# Make sure that $e is writeable by all. |# $B is the location of the `dates' program. |# Author : Oscar Nierstrasz @ ..!utcsrgv!oscar |e=/usr/pub/events |B=/usr/local |l=/tmp/lock_events |u="Usage: events [#days] | [-vi] | [-ed] | [-hed] | [-emacs]" |case $# in |0 ) arg=0 ;; |1 ) arg=$1 ;; |* ) echo "$u" 1>&2 | exit ;; |esac |case $arg in |-v ) ed=vi ;; |-vi ) ed=vi ;; |-h ) ed=hed ;; |-hed ) ed=hed ;; |-e ) ed=ed ;; |-ed ) ed=ed ;; |-em ) ed=emacs ;; |-emacs ) ed=emacs ;; |-rm ) rm -f $l | exit ;; |-* ) echo "events : unknown flag" 1>&2 | exit ;; |[0-9]* ) $B/dates -t $arg $e | exit ;; |* ) echo "$u" 1>&2 | exit ;; |esac | |umask 0777 |if (echo > $l) 2> /dev/null |then | umask 022 | $ed $e | rm -f $l |else | echo "Sorry, someone is already editing the events file!" 1>&2 |fi Rosebud chmod 0755 events echo hi sed 's/^|//' > hi << 'Rosebud' |#! /bin/sh |# hi user msg |# Write a message on every tty that a user is logged onto. |# With the -m flag, the message will be mailed if the user |# is not logged in (useful in shell scripts). |# Author : Oscar Nierstrasz @ ..!utcsrgv!oscar | |case $# in |0 ) echo "Usage: hi user [-m] msg" | echo " hi user -f <file> ..." | echo " <cmd> | hi user" | exit ;; |esac |u=$1 |shift |case $# in |0 ) f=/tmp/hi$$ | cat > $f ;; |1 ) ;; |* ) case $1 in | -f ) shift | f=$* ;; | -m ) shift | m=y ;; | esac ;; |esac |w=`who | fgrep $u` |case $w in |"" ) case $m in | y ) echo "$*" | mail $u ;; | *) echo "$u not logged in" ;; | esac ;; |* ) case $f in | "" ) echo "$w" | sed -e "s%^.*tty%/dev/tty%" -e "s/ .*$//" \ | -e "s/^/echo \"$*\" > /" | /bin/sh ;; | * ) echo "$w" | sed -e "s%^.*tty%/dev/tty%" -e "s/ .*$//" \ | -e "s%^%cat $f > %" | /bin/sh ;; | esac ;; |esac |rm -f /tmp/hi$$ Rosebud chmod 0755 hi echo remind sed 's/^|//' > remind << 'Rosebud' |#! /bin/sh |# remind [time] [-m|-c] [message] ... 840214 |# Echo a message at a later time. |# With the -m flag, `mail' is used if you have logged off. |# With the -c flag, the message is executed as a command. |# Calls the shell script `hi' to get you wherever you are! |# SEE ALSO : at(1), leave(1). |# Author : Oscar Nierstrasz @ utcsrgv!oscar |C=/u3/oscar/cmd |case $# in |0 ) w='{ split($4, from, ":") ; print from[1] ":" from[2] }' | echo "It's" `date | awk "$w"` | echo -n "When shall I call you? : " | read t ;; |* ) t=$1 | shift ;; |esac |case $t in |[0-9] ) t=$t:00 ;; |[0-1][0-9] ) t=$t:00 ;; |2[0-4] ) t=$t:00 ;; |[0-9]:[0-5][0-9] ) ;; |[0-1][0-9]:[0-5][0-9] ) ;; |2[0-9]:[0-5][0-9] ) ;; |* ) echo "Enter time as hh or hh:mm" | exit ;; |esac |case $# in |0 ) echo -n "Message? : " | read m ;; |* ) case $1 in | -c ) shift | c=$* ;; | -m ) hi=-m | shift | m=$* ;; | * ) m=$* ;; | esac ;; |esac |case $c in |"" ) case $m in | "" ) m="Hi! -- It's $t" ;; | * ) m="It's $t -- $m" ;; | esac ;; |esac |# Awk script: |w='NR == 1 { split($4, from, ":") } |NR == 2 { split($1, to, ":") } |END { | hrs = to[1] - from[1] | mins = to[2] - from[2] | time = 60 * hrs + mins | if (time < 0) { | time += 60 * 12 | if ((to[1] > 12) || (time < 0)) | time += 60 * 12 | } | print 60 * time | }' |s=`(date ; echo $t ) | awk "$w"` |case $s in |60 ) ;; |* ) x="s" ;; |esac |echo "See you in" `expr $s / 60` "minute$x ..." |case $c in |"" ) (sleep $s ; $C/hi $USER $hi "$m") & ;; |* ) (sleep $s ; echo "$c" | /bin/sh) & ;; |esac Rosebud chmod 0755 remind