Xref: utzoo comp.mail.misc:2294 comp.unix.xenix:7229 Path: utzoo!attcan!utgpu!jarvis.csri.toronto.edu!mailrus!ames!sun-barr!newstop!sun!eureka!argv From: argv%eureka@Sun.COM (Dan Heller) Newsgroups: comp.mail.misc,comp.unix.xenix Subject: Re: Checking for new mail Message-ID: <122772@sun.Eng.Sun.COM> Date: 21 Aug 89 17:00:32 GMT References: <105@csnz.co.nz> Sender: news@sun.Eng.Sun.COM Reply-To: argv@sun.UUCP (Dan Heller) Lines: 565 In article <105@csnz.co.nz> paul@csnz.co.nz (Paul Gillingwater) writes: > Hi, > What's the best way to check for the arrival of new mail? > I'm writing in C using SCO Xenix 2.3.1, but no doubt other approaches > Any ideas? Code fragments would be appreciated.... Someone asked if you had biff, and I don't think you do. Even if you did, biff's biggest problem is that it ruin's your whole screen when new mail comes in and if you're in an editor, the newlines don't work right and it's just plain ugly. Sysline is probably the nicest utility for this type of thing because it writes to your status line (most smart terminals have a status line these days). However, sysline is not port- able to system V or xenix (since last I checked). (btw, does anyone know where that came from? I never saw it posted.) So, I wrote a program called "watch" which watches your mailbox and, in the event of new mail, it prints info about the mail (who it's from, when it arrived, and the subject) on the terminal's status line. For window systems, it writes it to the titlebar of your window (command line specification for this may be necessary depending on your window). There are many options which include printing the time and/or your current working directory, or anything you want on the status line. The last time I posted this (as part of the Dclock widget submission in comp.sources.x), it was not portable (strictly BSD). But, I just hacked it up to be sys-v/xenix compatible. See leading comments at the top of the file. I encourage people to make fixes and send me patches. /* * watch -- Simple program which watches for new mail (replaces obnoxious biff) * Ring bell and prints on status line (stdout if no status line) the * author of the mail and the subject ( "(No Subject)" if none). * * Author: Dan Heller island!argv@sun.com * Based on a great idea by Akkana * * Automatcially exits when user logs out. * * The program runs in background by default. On startup, the process id of * the child is output to stdout. * * Options: * * -w for sunwindows (because you don't own your tty). This option need * not be given if you are running suntools. If you are rlogged into another * machine from a shelltool, then you will need to specify -w. * * -t displays the time whenever the status line is updated. If the timeout * between updates is one minute, then the time displayed will be accurate. * * -T timeout A timeout must be given -- this timeout is the number of * seconds to wait between updates. 30 is the minimum timeout. * * -f filename A filename must be specified. This file is read and its * contents (one line) displayed on the status line (after the time, if * specified). The purpose for this flag is to allow the user to have * his current working directory in the file. Whenever the user changes * directories, the current dir is put into the file, and the watch * program is sent a SIGALRM and the status line is updated. The alias * and related commands to accomplish this: * * % set watch = `watch` * % alias cd 'cd \!*; pwd >! ~/file; kill -ALRM $watch' * * The pid of the watch program is output and subsequently set in the * "watch" shell variable. You can send signals to the program by * referencing the $watch variable as the process id of the program. * * -ts "string" * -fs "string" * The strings are the escape sequences to go "to statusline" (-ts) * and "from statusline" respectively. This is provided for terminals * whose termcap entries don't have this information even tho it exists * or if you want to use an alternate status line (e.g. the top line of * your terminal). For example, the Wyse50's escape sequence to use the * top statusline is ESC-F (^[F) -- to return would be just a carriage * return (^M). If -ts is given and -fs is missing, -fs defaults to ^M. * * -m file * Uses "file" as mailfile rather than the defaut: /usr/spool/mail or * whatever MAILDIR is defined to be above. * * -d Turns on debugging. The program is not run in background. * * Hasn't been ported to sys-v or xenix, but the framework has been laid * out. To get started, #define SYSV ... * There may be some include file problems, and you're going to have a problem * with setitimer(). The current hack to get this to work for systems without * setitimer is to use sleep(2) instead. But this is untested -- if it doesn't * work, after the call to sleep(interval), call alrm(). * * Bugs: * If terminal has no status line capability, it prints to stdout which * can be annoying, but that's the user's fault for running it. * The mail format requires the "From " line format to separate messages. * MMDF users can't use this program without hacking it. * * compile: cc -O -s watch.c -ltermlib -o watch */ #define MAILDIR "/usr/spool/mail" #define UTMP "/etc/utmp" #ifdef SYSV #define rindex strrchr #define index strchr #else #define SYS_ERRLIST #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef sun #include #include extern char *sprintf(); #endif /* sun */ #ifndef MAXPATHLEN #define MAXPATHLEN BUFSIZ #endif /* MAXPATHLEN */ int debug, COLS = 80, iswindow, beeper, alrm(), print_time, hs; long last_size, last_access; char *from(), *Time(), *print_dir, *dir(); char spoolfile[MAXPATHLEN], bp[1024], ts[16], fs[16]; struct stat stbuf; #ifdef SYS_ERRLIST extern int errno; extern char *sys_errlist[]; #endif /* SYS_ERRLIST */ FILE *Tty; char *usage = "usage: %s [-d] [-w] [-t] [-T timeout] [-f file] [-m file] [-ts \"string\"] [-fs \"string\"]\n"; main(argc, argv) char **argv; { char *getenv(), *getname(), *ttyname(), *rindex(), *cmd, *tty; struct itimerval timer; int interval = 60, pid; if (cmd = rindex(*argv, '/')) cmd++; else cmd = *argv; #ifdef sun if (getenv("WINDOW_PARENT")) iswindow = 1; #endif /* sun */ while (*++argv) if (!strcmp(*argv, "-d")) debug = 1; else if (!strcmp(*argv, "-T")) { if (!*++argv) { printf("%s: -T requires time argument of >= 30 seconds.\n",cmd); exit(1); } if ((interval = atoi(*argv)) < 30 && !debug) printf("Minimum time interval is %d seconds.\n", interval = 30); } else if (!strcmp(*argv, "-w")) iswindow = 1; else if (!strcmp(*argv, "-t")) print_time = 1; else if (!strcmp(*argv, "-f")) if (!*++argv) printf("%s: -f requires filename argument.\n", cmd), exit(1); else print_dir = *argv; else if (!strcmp(*argv, "-m")) if (!*++argv) printf("%s: -m requires filename argument.\n", cmd), exit(1); else (void) strcpy(spoolfile, *argv); else if (!strcmp(*argv, "-ts")) { if (!*++argv) { printf("%s: -ts requires 'to statusline' sequence.\n", cmd); exit(1); } (void) strcpy(ts, *argv); (void) strcpy(fs, "\012"); } else if (!strcmp(*argv, "-fs")) { if (!*++argv) { printf("%s: -ts requires 'from statusline' sequence.\n", cmd); exit(1); } (void) strcpy(fs, *argv); } else fprintf(stderr, usage, cmd), exit(1); if (fs[0] && !ts[0]) printf("%s: -fs requires the -ts option be specified as well.\n", cmd); init_cadr(ts[0] != 0); beeper = beep(); if (spoolfile[0] == 0) (void) sprintf (spoolfile, "%s/%s", MAILDIR, getname()); /* get the tty name */ if (isatty(fileno(stderr))) tty = ttyname(fileno(stderr)); else if (isatty(fileno(stdout))) tty = ttyname(fileno(stdout)); else if (isatty(fileno(stdin))) tty = ttyname(fileno(stdin)); else fprintf(stderr, "You don't have to redirect *everything*!\n"), exit(1); if (!debug && (pid = fork())) { /* Parent goes into the background */ printf("%d\n", pid); /* print the pid of the child */ fflush(stdout); exit(0); } (void) signal(SIGINT, SIG_IGN); (void) signal(SIGQUIT, SIG_IGN); #ifdef SIGTSTP (void) signal(SIGTSTP, SIG_IGN); (void) signal(SIGCONT, SIG_IGN); #endif /* SIGTSTP */ (void) close(fileno(stdout)); (void) close(fileno(stderr)); sleep(1); /* sleep to give parent a chance to print pid */ if (!stat(spoolfile, &stbuf)) { last_size = stbuf.st_size; last_access = stbuf.st_atime; } (void) signal(SIGALRM, alrm); #ifdef ITIMER_REAL timer.it_value.tv_sec = interval; timer.it_value.tv_usec = 0; timer.it_interval.tv_sec = interval; timer.it_interval.tv_usec = 0; setitimer(ITIMER_REAL, &timer, 0); #endif /* ITIMER_REAL */ if (!(Tty = fopen(tty, "w"))) exit(1); tty += 5; /* get rid of "/dev/" */ alrm(); /* initialize */ while (still_logged_in(tty)) { #ifdef ITIMER_REAL pause(); #else /* ITIMER_REAL */ sleep(interval); /* alrm(); */ #endif /* ITIMER_REAL */ } fprintf(Tty, "Exiting at %s\n", Time()); } alrm() { char buf[256], mail_msg[256]; register char *dir_p = "", *time_p = ""; char *fromp; static unread; int do_print = 0; /* if there's mail, set do_print to guarantee dir() to give the right * information. read mail into tmp buffer. */ *mail_msg = 0; if (!stat(spoolfile, &stbuf)) { if (stbuf.st_size > last_size && *(fromp = from(last_size))) { do_print = 1; bell(&beeper); (void) sprintf(mail_msg, "New mail %s", fromp); unread = 2; } else if (stbuf.st_atime > last_access) { last_access = stbuf.st_atime; if (hs && unread) do_print = 1; unread = 0; } else if (unread) { if (unread > 1 && hs) unread = do_print = 1; (void) strcpy(mail_msg, "(mail)"); } last_size = stbuf.st_size; } if (print_time) time_p = Time(), do_print = 1; if (print_dir) dir_p = dir(&do_print); if (do_print) { (void) sprintf(buf, "%s%s %s", time_p, dir_p, mail_msg); fprintf(Tty, "%s%.*s%s", ts, COLS-2 - strlen(ts) - strlen(fs), buf, fs); fflush(Tty); } } char * dir(force) int *force; { FILE *fp; static char buf[256], buf2[256]; char *p; extern char *index(); (void) strcpy(buf2, buf); if (!(fp = fopen(print_dir, "r"))) return NULL; p = fgets(buf, sizeof buf, fp); fclose(fp); if (!p || !*force && !strcmp(buf, buf2)) return NULL; *force = 1; if (p = index(buf, '\n')) *p = 0; return buf; } bell(ding) register int *ding; { #ifdef KIOCCMD if (*ding != -1) { int cmd = KBD_CMD_BELL; struct timeval timer; timer.tv_sec = 0; timer.tv_usec = 100000; if (ioctl(*ding, KIOCCMD, &cmd)) *ding = -1; select(32, 0,0,0, &timer); cmd = KBD_CMD_NOBELL; if (ioctl(*ding, KIOCCMD, &cmd)) *ding = -1; } else #endif /* KIOCCMD */ putchar(7), fflush(Tty); } /* return username: username doesn't have to be declared static cuz funcs * that get the username points to static data (bug, but I know what I'm * doing). */ char * getname() { char *username, *getlogin(); if (!(username = getlogin())) { struct passwd *getpwuid(), *pwent; if (!(pwent = getpwuid(getuid()))) { perror("getpwuid"); fprintf(stderr, "I don't know you. Try using -m \n"); exit(0); } username = pwent->pw_name; endpwent(); /* close the passwd file */ } return username; } /* Explanation of this silly routine: * return the fd of /dev/kbd if we're not rlogged in or from a pseudo-tty. * if return -1, bell() will just putchar(7), else it'll do an ioctl * to /dev/kbd to ring the bell on fd "kbd". Note, this is sun-specific * cuz there are problems with "ringing the bell" on certain shell-like * applications on the sun... */ beep() { int kbd = -1; #ifdef sun char *getenv(), *p = getenv("TERM"); if (!iswindow && p && !strcmp(p, "sun")) { kbd = open("/dev/kbd", O_WRONLY, 0); if (debug) fprintf(Tty, "%d\n", kbd); } #endif /* sun */ return kbd; } init_cadr(ts_known) { char *getenv(), *tgetstr(), *termname; char *tmp[1]; if (ts_known) { termname = getenv("TERM"); COLS = 80; return; } if (!(termname = getenv("TERM"))) { puts("No terminal type!"); goto no_type; } if (tgetent(bp, termname) <= 0) { perror(termname); COLS = 80; } else COLS = tgetnum("co"); if (debug) fprintf(Tty, "Terminal type = \"%s\"\n", termname); if (hs = tgetflag("hs")) { tmp[0] = ts; tgetstr("ts", tmp); tmp[0] = fs; tgetstr("fs", tmp); #ifdef sun } else if (!strncmp(termname, "sun", 3) || iswindow) { struct ttysize win; if (!ioctl(0, TIOCGSIZE, &win)) COLS = win.ts_cols; /* if iswindow is false, user is not running suntools */ if (iswindow || !strncmp(ttyname(0), "/dev/ttyp", 9)) { iswindow = hs = 1; (void) strcpy(ts, "\033]l"); (void) strcpy(fs, "\033\\"); } #endif /* sun */ } else if (!strncmp(termname, "wy", 2)) { /* bad hack for wyse's */ (void) strcpy(ts, "\033f"); (void) strcpy(fs, "\r"); hs = 1; } else { no_type: puts("No status line capability -- output to stdout"); (void) strcpy(ts, " "); (void) strcpy(fs, "\r\n"); } if (debug) bell(&beeper), fprintf(Tty, "%sThis is a test!%s", ts, fs); } char * Time() { static char time_buf[11]; struct tm *T; long x; time(&x), T = localtime(&x); (void) sprintf(time_buf, "%d:%02d ", (T->tm_hour) ? ((T->tm_hour <= 12) ? T->tm_hour : T->tm_hour - 12) : 12, T->tm_min); return time_buf; } char * from(offset) register long offset; { static char buf[256]; char buf2[128]; FILE *fp; register char *p; int nomatch = 1; (void) strcpy(buf, "From \"unknown\""); if (!(fp = fopen(spoolfile, "r")) || fseek(fp, offset, 0L)) { #ifdef SYS_ERRLIST (void) strcpy(buf, sys_errlist[errno]); #else perror(spoolfile); #endif /* SYS_ERRLIST */ goto the_end; } while (fgets(buf, sizeof(buf), fp) && (nomatch = strncmp(buf, "From ", 5))) ; if (nomatch) { buf[0] = 0; goto the_end; } if (p = index(buf+5, ' ')) *p = 0; else goto the_end; p += strlen(strcpy(p, " -> ")); while (fgets(buf2, sizeof(buf2), fp)) if (*buf2 == '\n') { (void) sprintf(p, " (No Subject)"); break; } else if (sscanf(buf2, "Subject: %[^\n]s", p)) break; the_end: fclose(fp); /* Reset the access time of the spool file to prevent * bogus "new mail" messages from the shell. */ { #ifdef ITIMER_REAL struct timeval times[2]; times[0].tv_sec = stbuf.st_atime; times[0].tv_usec = 0; times[1].tv_sec = stbuf.st_mtime; times[1].tv_usec = 0; if (!strncmp(MAILDIR, spoolfile, strlen(MAILDIR)) && utimes(spoolfile, times)) perror("utime"); #else long times[2]; times[0] = stbuf.st_atime; times[1] = stbuf.st_mtime; if (!strncmp(MAILDIR, spoolfile, strlen(MAILDIR)) && utime(mailfile, times)) perror("utime"); #endif /* ITIMER_REAL */ } return buf; } /* * return 1 if user is still logged in this tty. 0 otherwise. * It could be that the user logs out and then back in on the * same tty before this gets called again (especially if you're * running in a window-based environment like suns or X windows). */ still_logged_in(tty) char *tty; { struct utmp buf; static int fd; static long pos; /* once our position in utmp is found, always seek there */ if (!fd && (fd = open(UTMP, 0)) == -1) return 0; if (lseek(fd, pos, 0) == -1) return 0; while (read(fd, (char *) &buf, sizeof buf) == sizeof buf) if (buf.ut_line[0] && !strcmp(buf.ut_line, tty)) break; else { if (debug && buf.ut_line[0]) fprintf(Tty, "%s <-> %s\n", buf.ut_line, tty); pos += sizeof buf; } /* it's impossible to exit this loop without making a match */ return buf.ut_name[0] != 0; } dan ----- My postings reflect my opinion only -- not the opinion of any company.