Path: utzoo!utgpu!jarvis.csri.toronto.edu!cs.utexas.edu!uunet!mcsun!sunic!tut!kannel!kim From: kim@kannel.lut.fi (Kimmo Suominen) Newsgroups: comp.sys.hp Subject: Re: FINGER HP800's (?) Message-ID: Date: 4 Dec 89 15:10:11 GMT References: <47500018@uxe.cso.uiuc.edu> Sender: kim@kannel.lut.fi Distribution: comp Organization: Lappeenranta University of Technology, Finland Lines: 1213 In-reply-to: saaf@joker.optics.rochester.edu's message of 1 Dec 89 15:10:33 GMT I thought a few minutes later that you would propablu need this too. They are both originally from hemuli.atk.vtt.fi, but I've made some changes. Use -DBSD43 when you compile this. Add the resolver library if you want to (if you have it). Watch out for the signature and other comments in the end. --- CUT HERE --- CUT HERE --- CUT HERE --- CUT HERE --- CUT HERE --- /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #ifndef lint char copyright[] = "@(#) Copyright (c) 1980 Regents of the University of California.\n\ All rights reserved.\n"; #endif not lint #ifndef lint static char sccsid[] = "@(#)finger.c 5.8 (Berkeley) 3/13/86"; #endif not lint /* * This is a finger program. It prints out useful information about users * by digging it up from various system files. It is not very portable * because the most useful parts of the information (the full user name, * office, and phone numbers) are all stored in the VAX-unused gecos field * of /etc/passwd, which, unfortunately, other UNIXes use for other things. * * There are three output formats, all of which give login name, teletype * line number, and login time. The short output format is reminiscent * of finger on ITS, and gives one line of information per user containing * in addition to the minimum basic requirements (MBR), the full name of * the user, his idle time and office location and phone number. The * quick style output is UNIX who-like, giving only name, teletype and * login time. Finally, the long style output give the same information * as the short (in more legible format), the home directory and shell * of the user, and, if it exits, a copy of the file .plan in the users * home directory. Finger may be called with or without a list of people * to finger -- if no list is given, all the people currently logged in * are fingered. * * The program is validly called by one of the following: * * finger {short form list of users} * finger -l {long form list of users} * finger -b {briefer long form list of users} * finger -q {quick list of users} * finger -i {quick list of users with idle times} * finger namelist {long format list of specified users} * finger -s namelist {short format list of specified users} * finger -w namelist {narrow short format list of specified users} * * where 'namelist' is a list of users login names. * The other options can all be given after one '-', or each can have its * own '-'. The -f option disables the printing of headers for short and * quick outputs. The -b option briefens long format outputs. The -p * option turns off plans for long format outputs. */ /* * Change by pst@anise.acc.com (Paul Traina): show only one instance * of a user on long displays. */ /* * Changed to read wtmp backwards by raussi@kannel.lut.fi (Heikki Raussi). */ /* * Added organization and cleaned up output. */ #include #include #include #include #include #include #include #if BSD #include #endif #include #include #include #include #include #define bcopy(s,d,n) memcpy(d,s,n) #define index(s,c) strchr(s,c) #define rindex(s,c) strrchr(s,c) #define ASTERISK '*' /* ignore this in real name */ #define COMMA ',' /* separator in pw_gecos field */ #define COMMAND '-' /* command line flag char */ #if UCB #define CORY 'C' /* cory hall office */ #define EVANS 'E' /* evans hall office */ #endif #define SAMENAME '&' /* repeat login name in real name */ #define TALKABLE 0220 /* tty is writable if 220 mode */ #define ORGANIZATION "Lappeenranta University of Technology, Computing Centre, Finland\n\n" struct utmp user; #define NMAX sizeof(user.ut_name) #define LMAX sizeof(user.ut_line) #if BSD #define HMAX sizeof(user.ut_host) #endif struct person { /* one for each person fingered */ char *name; /* name */ char tty[LMAX+1]; /* null terminated tty line */ #if BSD char host[HMAX+1]; /* null terminated remote host name */ #endif long loginat; /* time of (last) login */ long idletime; /* how long idle (if logged in) */ char *realname; /* pointer to full name */ char *office; /* pointer to office name */ char *officephone; /* pointer to office phone no. */ char *homephone; /* pointer to home phone no. */ char *random; /* for any random stuff in pw_gecos */ struct passwd *pwd; /* structure of /etc/passwd stuff */ char loggedin; /* person is logged in */ char writable; /* tty is writable */ char original; /* this is not a duplicate entry */ struct person *link; /* link to next person */ }; #if BSD char LASTLOG[] = "/usr/adm/lastlog"; /* last login info */ #endif char USERLOG[] = "/etc/utmp"; /* who is logged in */ char PLAN[] = "/.plan"; /* what plan file is */ char PROJ[] = "/.project"; /* what project file */ int unbrief = 1; /* -b option default */ int header = 1; /* -f option default */ int hack = 1; /* -h option default */ int idle = 0; /* -i option default */ int large = 0; /* -l option default */ int match = 1; /* -m option default */ int plan = 1; /* -p option default */ int unquick = 1; /* -q option default */ int small = 0; /* -s option default */ int wide = 1; /* -w option default */ int unshort; #if BSD int lf; /* LASTLOG file descriptor */ #endif struct person *person1; /* list of people */ long tloc; /* current time */ struct passwd *pwdcopy(); char *strcpy(); char *malloc(); char *ctime(); int fdwt; /* hege */ main(argc, argv) int argc; register char **argv; { FILE *fp; register char *s; /* parse command line for (optional) arguments */ while (*++argv && **argv == COMMAND) for (s = *argv + 1; *s; s++) switch (*s) { case 'b': unbrief = 0; break; case 'f': header = 0; break; case 'h': hack = 0; break; case 'i': idle = 1; unquick = 0; break; case 'l': large = 1; break; case 'm': match = 0; break; case 'p': plan = 0; break; case 'q': unquick = 0; break; case 's': small = 1; break; case 'w': wide = 0; break; default: fprintf(stderr, "Usage: finger [-bfhilmpqsw] [login1 [login2 ...] ]\n"); exit(1); } if (unquick || idle) time(&tloc); /* * *argv == 0 means no names given */ if (*argv == 0) doall(); else donames(argv); if (person1) print(); exit(0); } doall() { register struct person *p; register struct passwd *pw; int uf; char name[NMAX + 1]; unshort = large; if ((uf = open(USERLOG, 0)) < 0) { fprintf(stderr, "finger: error opening %s\n", USERLOG); exit(2); } if (unquick) { #if BSD extern _pw_stayopen; #endif setpwent(); #if BSD _pw_stayopen = 1; #endif fwopen(); } while (read(uf, (char *)&user, sizeof user) == sizeof user) { if (user.ut_name[0] == 0) continue; #if !BSD if (user.ut_type != USER_PROCESS) continue; #endif if (person1 == 0) p = person1 = (struct person *) malloc(sizeof *p); else { p->link = (struct person *) malloc(sizeof *p); p = p->link; } bcopy(user.ut_name, name, NMAX); name[NMAX] = 0; bcopy(user.ut_line, p->tty, LMAX); p->tty[LMAX] = 0; #if BSD bcopy(user.ut_host, p->host, HMAX); p->host[HMAX] = 0; #endif p->loginat = user.ut_time; p->pwd = 0; p->loggedin = 1; if (unquick && (pw = getpwnam(name))) { p->pwd = pwdcopy(pw); decode(p); p->name = p->pwd->pw_name; } else p->name = strcpy(malloc(strlen(name) + 1), name); } if (unquick) { fwclose(); endpwent(); } close(uf); if (person1 == 0) { printf("No one logged on\n"); return; } p->link = 0; } donames(argv) char **argv; { register struct person *p; register struct passwd *pw; int uf; /* * get names from command line and check to see if they're * logged in */ unshort = !small; for (; *argv != 0; argv++) { if (netfinger(*argv)) continue; if (person1 == 0) p = person1 = (struct person *) malloc(sizeof *p); else { p->link = (struct person *) malloc(sizeof *p); p = p->link; } p->name = *argv; p->loggedin = 0; p->original = 1; p->pwd = 0; } if (person1 == 0) return; p->link = 0; /* * if we are doing it, read /etc/passwd for the useful info */ if (unquick) { setpwent(); if (!match) { #if BSD extern _pw_stayopen; _pw_stayopen = 1; #endif for (p = person1; p != 0; p = p->link) if (pw = getpwnam(p->name)) p->pwd = pwdcopy(pw); } else while ((pw = getpwent()) != 0) { for (p = person1; p != 0; p = p->link) { if (!p->original) continue; if (strcmp(p->name, pw->pw_name) != 0 && !matchcmp(pw->pw_gecos, pw->pw_name, p->name)) continue; if (p->pwd == 0) p->pwd = pwdcopy(pw); else { struct person *new; /* * handle multiple login names, insert * new "duplicate" entry behind */ new = (struct person *) malloc(sizeof *new); new->pwd = pwdcopy(pw); new->name = p->name; new->original = 1; new->loggedin = 0; new->link = p->link; p->original = 0; p->link = new; p = new; } } } endpwent(); } /* Now get login information */ if ((uf = open(USERLOG, 0)) < 0) { fprintf(stderr, "finger: error opening %s\n", USERLOG); exit(2); } while (read(uf, (char *)&user, sizeof user) == sizeof user) { if (*user.ut_name == 0) continue; #if !BSD if (user.ut_type != USER_PROCESS) continue; #endif for (p = person1; p != 0; p = p->link) { if (p->loggedin == 2) continue; if (strncmp(p->pwd ? p->pwd->pw_name : p->name, user.ut_name, NMAX) != 0) continue; if (p->loggedin == 0) { bcopy(user.ut_line, p->tty, LMAX); p->tty[LMAX] = 0; #if BSD bcopy(user.ut_host, p->host, HMAX); p->host[HMAX] = 0; #endif p->loginat = user.ut_time; p->loggedin = 1; } else { /* p->loggedin == 1 */ struct person *new; new = (struct person *) malloc(sizeof *new); new->name = p->name; bcopy(user.ut_line, new->tty, LMAX); new->tty[LMAX] = 0; #if BSD bcopy(user.ut_host, new->host, HMAX); new->host[HMAX] = 0; #endif new->loginat = user.ut_time; new->pwd = p->pwd; new->loggedin = 1; new->original = 0; new->link = p->link; p->loggedin = 2; p->link = new; p = new; } } } close(uf); if (unquick) { fwopen(); for (p = person1; p != 0; p = p->link) decode(p); fwclose(); } } print() { register FILE *fp; register struct person *p, *q; register char *s; register c; register long minidle; /* * print out what we got */ if (header) { printf(ORGANIZATION); /* kim */ if (unquick) { if (!unshort) if (wide) printf("Login Name TTY Idle When Office\n"); else printf("Login TTY Idle When Office\n"); } else { printf("Login TTY When"); if (idle) printf(" Idle"); putchar('\n'); } } for (p = person1; p != 0; p = p->link) { if (!unquick) { quickprint(p); continue; } if (!unshort) { shortprint(p); continue; } #define min(a,b) ((a) < (b) ? (a) : (b)) for (q = person1, minidle = (((unsigned)~0)>>1); q != 0; q = q->link) if (!strcmp(q->name, p->name)) minidle = min(minidle, q->idletime); if (p->idletime > minidle) continue; personprint(p); if (p->pwd != 0) { if (hack) { s = malloc(strlen(p->pwd->pw_dir) + sizeof PROJ); strcpy(s, p->pwd->pw_dir); strcat(s, PROJ); if ((fp = fopen(s, "r")) != 0) { printf("Project: "); while ((c = getc(fp)) != EOF) { if (c == '\n') break; if (isprint(c) || isspace(c)) putchar(c); else putchar('?'); } fclose(fp); putchar('\n'); } free(s); } if (plan) { s = malloc(strlen(p->pwd->pw_dir) + sizeof PLAN); strcpy(s, p->pwd->pw_dir); strcat(s, PLAN); if ((fp = fopen(s, "r")) == 0) printf("No Plan.\n"); else { printf("Plan:\n"); while ((c = getc(fp)) != EOF) if (isprint(c) || isspace(c)) putchar(c); else putchar('?'); fclose(fp); } free(s); } } if (p->link != 0) putchar('\n'); } } /* * Duplicate a pwd entry. * Note: Only the useful things (what the program currently uses) are copied. */ struct passwd * pwdcopy(pfrom) register struct passwd *pfrom; { register struct passwd *pto; pto = (struct passwd *) malloc(sizeof *pto); #define savestr(s) strcpy(malloc(strlen(s) + 1), s) pto->pw_name = savestr(pfrom->pw_name); pto->pw_uid = pfrom->pw_uid; pto->pw_gecos = savestr(pfrom->pw_gecos); pto->pw_dir = savestr(pfrom->pw_dir); pto->pw_shell = savestr(pfrom->pw_shell); #undef savestr return pto; } /* * print out information on quick format giving just name, tty, login time * and idle time if idle is set. */ quickprint(pers) register struct person *pers; { printf("%-*.*s ", NMAX, NMAX, pers->name); if (pers->loggedin) { if (idle) { findidle(pers); printf("%c%-*s %-16.16s", pers->writable ? ' ' : '*', LMAX, pers->tty, ctime(&pers->loginat)); ltimeprint(" ", &pers->idletime, ""); } else printf(" %-*s %-16.16s", LMAX, pers->tty, ctime(&pers->loginat)); putchar('\n'); } else printf(" Not Logged In\n"); } /* * print out information in short format, giving login name, full name, * tty, idle time, login time, office location and phone. */ shortprint(pers) register struct person *pers; { char *p; char dialup; if (pers->pwd == 0) { printf("%-15s ???\n", pers->name); return; } printf("%-*s", NMAX, pers->pwd->pw_name); dialup = 0; if (wide) { if (pers->realname) printf(" %-20.20s", pers->realname); else printf(" ??? "); } putchar(' '); if (pers->loggedin && !pers->writable) putchar('*'); else putchar(' '); if (*pers->tty) { if (pers->tty[0] == 't' && pers->tty[1] == 't' && pers->tty[2] == 'y') { if (pers->tty[3] == 'd' && pers->loggedin) dialup = 1; printf("%-3.3s ", pers->tty + 3); } else printf("%-3.3s ", pers->tty); } else printf(" "); p = ctime(&pers->loginat); if (pers->loggedin) { stimeprint(&pers->idletime); printf(" %3.3s %-5.5s ", p, p + 11); } else if (pers->loginat == 0) printf(" < . . . . >"); else if (tloc - pers->loginat >= 180 * 24 * 60 * 60) printf(" <%-6.6s, %-4.4s>", p + 4, p + 20); else printf(" <%-12.12s>", p + 4); if (dialup && pers->homephone) printf(" %20s", pers->homephone); else { if (pers->office) printf(" %-11.11s", pers->office); else if (pers->officephone || pers->homephone) printf(" "); if (pers->officephone) printf(" %s", pers->officephone); else if (pers->homephone) printf(" %s", pers->homephone); } putchar('\n'); } /* * print out a person in long format giving all possible information. * directory and shell are inhibited if unbrief is clear. */ personprint(pers) register struct person *pers; { if (pers->pwd == 0) { printf("Login name: %-10s\t\t\tIn real life: ???\n", pers->name); return; } printf("Login name: %-10s", pers->pwd->pw_name); if (pers->loggedin && !pers->writable) printf("\t(messages off)\t"); else printf("\t\t\t"); if (pers->realname) printf("In real life: %s", pers->realname); if (pers->office) { printf("\nOffice: %-.11s", pers->office); if (pers->officephone) { printf(", %s", pers->officephone); if (pers->homephone) printf("\t\t\tHome phone: %s", pers->homephone); else if (pers->random) printf("\t\t\t%s", pers->random); } else if (pers->homephone) printf("\t\t\t\tHome phone: %s", pers->homephone); else if (pers->random) printf("\t\t\t\t%s", pers->random); } else if (pers->officephone) { printf("\nPhone: %s", pers->officephone); if (pers->homephone) printf(", %s", pers->homephone); if (pers->random) printf(", %s", pers->random); } else if (pers->homephone) { printf("\nPhone: %s", pers->homephone); if (pers->random) printf(", %s", pers->random); } else if (pers->random) printf("\n%s", pers->random); if (unbrief) { printf("\nDirectory: %-25s", pers->pwd->pw_dir); if (*pers->pwd->pw_shell) printf("\tShell: %-s", pers->pwd->pw_shell); } if (pers->loggedin) { register char *ep = ctime(&pers->loginat); #if BSD if (*pers->host) { printf("\nOn since %15.15s on %s from %s", &ep[4], pers->tty, pers->host); ltimeprint("\n", &pers->idletime, " Idle Time"); } else #endif { printf("\nOn since %15.15s on %-*s", &ep[4], LMAX, pers->tty); ltimeprint("", &pers->idletime, " Idle Time"); } } else if (pers->loginat == 0) printf("\nNever logged in."); else if (tloc - pers->loginat > 180 * 24 * 60 * 60) { register char *ep = ctime(&pers->loginat); printf("\nLast login %10.10s, %4.4s on %s", ep, ep+20, pers->tty); #if BSD if (*pers->host) printf(" from %s", pers->host); #endif } else { register char *ep = ctime(&pers->loginat); printf("\nLast login %16.16s on %s", ep, pers->tty); #if BSD if (*pers->host) printf(" from %s", pers->host); #endif } putchar('\n'); } /* * very hacky section of code to format phone numbers. filled with * magic constants like 4, 7 and 10. */ char * phone(s, len, alldigits) register char *s; int len; char alldigits; { char fonebuf[15]; register char *p = fonebuf; register i; #if BSD if (!alldigits) #endif return (strcpy(malloc(len + 1), s)); #if BSD switch (len) { case 4: *p++ = ' '; *p++ = 'x'; *p++ = '2'; *p++ = '-'; for (i = 0; i < 4; i++) *p++ = *s++; break; case 5: *p++ = ' '; *p++ = 'x'; *p++ = *s++; *p++ = '-'; for (i = 0; i < 4; i++) *p++ = *s++; break; case 7: for (i = 0; i < 3; i++) *p++ = *s++; *p++ = '-'; for (i = 0; i < 4; i++) *p++ = *s++; break; case 10: for (i = 0; i < 3; i++) *p++ = *s++; *p++ = '-'; for (i = 0; i < 3; i++) *p++ = *s++; *p++ = '-'; for (i = 0; i < 4; i++) *p++ = *s++; break; case 0: return 0; default: return (strcpy(malloc(len + 1), s)); } *p++ = 0; return (strcpy(malloc(p - fonebuf), fonebuf)); #endif } /* * decode the information in the gecos field of /etc/passwd */ decode(pers) register struct person *pers; { char buffer[256]; register char *bp, *gp, *lp; int alldigits; int hasspace; int len; pers->realname = 0; pers->office = 0; pers->officephone = 0; pers->homephone = 0; pers->random = 0; if (pers->pwd == 0) return; gp = pers->pwd->pw_gecos; bp = buffer; if (*gp == ASTERISK) gp++; while (*gp && *gp != COMMA) /* name */ if (*gp == SAMENAME) { lp = pers->pwd->pw_name; if (islower(*lp)) *bp++ = toupper(*lp++); while (*bp++ = *lp++) ; bp--; gp++; } else *bp++ = *gp++; *bp++ = 0; if ((len = bp - buffer) > 1) pers->realname = strcpy(malloc(len), buffer); if (*gp == COMMA) { /* office */ gp++; hasspace = 0; bp = buffer; while (*gp && *gp != COMMA) { *bp = *gp++; if (*bp == ' ') hasspace = 1; /* leave 5 for Cory and Evans expansion */ if (bp < buffer + sizeof buffer - 6) bp++; } *bp = 0; len = bp - buffer; bp--; /* point to last character */ if (hasspace || len == 0) len++; #if UCB else if (*bp == CORY) { strcpy(bp, " Cory"); len += 5; } else if (*bp == EVANS) { strcpy(bp, " Evans"); len += 6; } #endif else len++; if (len > 1) pers->office = strcpy(malloc(len), buffer); } if (*gp == COMMA) { /* office phone */ gp++; bp = buffer; alldigits = 1; while (*gp && *gp != COMMA) { *bp = *gp++; if (!isdigit(*bp)) alldigits = 0; if (bp < buffer + sizeof buffer - 1) bp++; } *bp = 0; pers->officephone = phone(buffer, bp - buffer, alldigits); } if (*gp == COMMA) { /* home phone */ gp++; bp = buffer; alldigits = 1; while (*gp && *gp != COMMA) { *bp = *gp++; if (!isdigit(*bp)) alldigits = 0; if (bp < buffer + sizeof buffer - 1) bp++; } *bp = 0; pers->homephone = phone(buffer, bp - buffer, alldigits); } if (pers->loggedin) findidle(pers); else findwhen(pers); } /* * find the last log in of a user by checking the LASTLOG file. * the entry is indexed by the uid, so this can only be done if * the uid is known (which it isn't in quick mode) */ fwopen() { #if BSD if ((lf = open(LASTLOG, 0)) < 0) fprintf(stderr, "finger: %s open error\n", LASTLOG); #else fdwt=open(WTMP_FILE,0); /* Hege */ #endif } findwhen(pers) register struct person *pers; { #if BSD struct lastlog ll; int i; if (lf >= 0) { lseek(lf, (long)pers->pwd->pw_uid * sizeof ll, 0); if ((i = read(lf, (char *)&ll, sizeof ll)) == sizeof ll) { bcopy(ll.ll_line, pers->tty, LMAX); pers->tty[LMAX] = 0; bcopy(ll.ll_host, pers->host, HMAX); pers->host[HMAX] = 0; pers->loginat = ll.ll_time; } else { if (i != 0) fprintf(stderr, "finger: %s read error\n", LASTLOG); pers->tty[0] = 0; pers->host[0] = 0; pers->loginat = 0L; } } else { pers->tty[0] = 0; pers->host[0] = 0; pers->loginat = 0L; } #else /* Hege */ struct utmp *wtmp; int done=1,ok=0; wtmp=(struct utmp *)malloc(sizeof(struct utmp)); if(lseek(fdwt,0l,2)==-1) done=0; while(done && (lseek(fdwt,(long)(sizeof(struct utmp)*-1),1)>0)) { done=read(fdwt,wtmp,sizeof(struct utmp)); wtmp->ut_user[8]='\0'; if(!strcmp(pers->name,wtmp->ut_user) && wtmp->ut_type==USER_PROCESS){ done=0; ok=1; } if(lseek(fdwt,(long)(sizeof(struct utmp)*-1),1)<0) done=0; } if (ok) { memcpy(pers->tty, wtmp->ut_line, LMAX); pers->tty[LMAX] = 0; pers->loginat = wtmp->ut_time; } else { pers->tty[0] = 0; pers->loginat = 0L; } #endif } fwclose() { #if BSD if (lf >= 0) close(lf); #else close(fdwt); /* Hege */ #endif } /* * find the idle time of a user by doing a stat on /dev/tty??, * where tty?? has been gotten from USERLOG, supposedly. */ findidle(pers) register struct person *pers; { struct stat ttystatus; static char buffer[20] = "/dev/"; long t; #define TTYLEN 5 strcpy(buffer + TTYLEN, pers->tty); buffer[TTYLEN+LMAX] = 0; if (stat(buffer, &ttystatus) < 0) { fprintf(stderr, "finger: Can't stat %s\n", buffer); exit(4); } time(&t); if (t < ttystatus.st_atime) pers->idletime = 0L; else pers->idletime = t - ttystatus.st_atime; pers->writable = (ttystatus.st_mode & TALKABLE) == TALKABLE; } /* * print idle time in short format; this program always prints 4 characters; * if the idle time is zero, it prints 4 blanks. */ stimeprint(dt) long *dt; { register struct tm *delta; delta = gmtime(dt); if (delta->tm_yday == 0) if (delta->tm_hour == 0) if (delta->tm_min == 0) printf(" "); else printf(" %2d", delta->tm_min); else if (delta->tm_hour >= 10) printf("%3d:", delta->tm_hour); else printf("%1d:%02d", delta->tm_hour, delta->tm_min); else printf("%3dd", delta->tm_yday); } /* * print idle time in long format with care being taken not to pluralize * 1 minutes or 1 hours or 1 days. * print "prefix" first. */ ltimeprint(before, dt, after) long *dt; char *before, *after; { register struct tm *delta; delta = gmtime(dt); if (delta->tm_yday == 0 && delta->tm_hour == 0 && delta->tm_min == 0 && delta->tm_sec <= 10) return (0); printf("%s", before); if (delta->tm_yday >= 10) printf("%d days", delta->tm_yday); else if (delta->tm_yday > 0) printf("%d day%s %d hour%s", delta->tm_yday, delta->tm_yday == 1 ? "" : "s", delta->tm_hour, delta->tm_hour == 1 ? "" : "s"); else if (delta->tm_hour >= 10) printf("%d hours", delta->tm_hour); else if (delta->tm_hour > 0) printf("%d hour%s %d minute%s", delta->tm_hour, delta->tm_hour == 1 ? "" : "s", delta->tm_min, delta->tm_min == 1 ? "" : "s"); else if (delta->tm_min >= 10) printf("%2d minutes", delta->tm_min); else if (delta->tm_min == 0) printf("%2d seconds", delta->tm_sec); else printf("%d minute%s %d second%s", delta->tm_min, delta->tm_min == 1 ? "" : "s", delta->tm_sec, delta->tm_sec == 1 ? "" : "s"); printf("%s", after); } matchcmp(gname, login, given) register char *gname; char *login; char *given; { char buffer[100]; register char *bp, *lp; register c; if (*gname == ASTERISK) gname++; lp = 0; bp = buffer; for (;;) switch (c = *gname++) { case SAMENAME: for (lp = login; bp < buffer + sizeof buffer && (*bp++ = *lp++);) ; bp--; break; case ' ': case COMMA: case '\0': *bp = 0; if (namecmp(buffer, given)) return (1); if (c == COMMA || c == 0) return (0); bp = buffer; break; default: if (bp < buffer + sizeof buffer) *bp++ = c; } /*NOTREACHED*/ } namecmp(name1, name2) register char *name1, *name2; { register c1, c2; for (;;) { c1 = *name1++; if (islower(c1)) c1 = toupper(c1); c2 = *name2++; if (islower(c2)) c2 = toupper(c2); if (c1 != c2) break; if (c1 == 0) return (1); } if (!c1) { for (name2--; isdigit(*name2); name2++) ; if (*name2 == 0) return (1); } else if (!c2) { for (name1--; isdigit(*name1); name1++) ; if (*name2 == 0) return (1); } return (0); } netfinger(name) char *name; { char *host; char fname[100]; struct hostent *hp; struct servent *sp; struct sockaddr_in sin; int s; register FILE *f; register int c; register int lastc; if (name == NULL) return (0); host = rindex(name, '@'); if (host == NULL) return (0); *host++ = 0; hp = gethostbyname(host); if (hp == NULL) { static struct hostent def; static struct in_addr defaddr; #if BSD43 static char *alist[1]; #endif static char namebuf[128]; defaddr.s_addr = inet_addr(host); if (defaddr.s_addr == -1) { printf("unknown host: %s\n", host); return (1); } strcpy(namebuf, host); def.h_name = namebuf; #if BSD43 def.h_addr_list = alist; #endif def.h_addr = (char *)&defaddr; def.h_length = sizeof (struct in_addr); def.h_addrtype = AF_INET; def.h_aliases = 0; hp = &def; } printf("[%s]", hp->h_name); sp = getservbyname("finger", "tcp"); if (sp == 0) { printf("tcp/finger: unknown service\n"); return (1); } sin.sin_family = hp->h_addrtype; bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length); sin.sin_port = sp->s_port; s = socket(hp->h_addrtype, SOCK_STREAM, 0); if (s < 0) { fflush(stdout); perror("socket"); return (1); } if (connect(s, (char *)&sin, sizeof (sin)) < 0) { fflush(stdout); perror("connect"); close(s); return (1); } printf("\n"); if (large) write(s, "/W ", 3); write(s, name, strlen(name)); write(s, "\r\n", 2); f = fdopen(s, "r"); while ((c = getc(f)) != EOF) { switch(c) { case 0210: case 0211: case 0212: case 0214: c -= 0200; break; case 0215: c = '\n'; break; } lastc = c; if (isprint(c) || isspace(c)) putchar(c); else putchar('?'); } if (lastc != '\n') putchar('\n'); (void)fclose(f); return (1); } --- CUT AGAIN --- CUT AGAIN --- CUT AGAIN --- CUT AGAIN --- CUT AGAIN --- This will get you along. It doesn't check mail (yet, I'm working on it), but it does show your last login CORRECTLY. The one in hemuli shows actually your "first login". If there's interest, I'll post or e-mail the improved version with mail checking. Kim -- ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, ( Kimmo Suominen ! Lappeenranta U of Technology ! kim@kannel.lut.fi ) ( "That's what I think" ! Computing Centre * Finland ! Funet: KUULA::KIM ) ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''