last.c revision 96785
11590Srgrimes/* 21590Srgrimes * Copyright (c) 1987, 1993, 1994 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 3. All advertising materials mentioning features or use of this software 141590Srgrimes * must display the following acknowledgement: 151590Srgrimes * This product includes software developed by the University of 161590Srgrimes * California, Berkeley and its contributors. 171590Srgrimes * 4. Neither the name of the University nor the names of its contributors 181590Srgrimes * may be used to endorse or promote products derived from this software 191590Srgrimes * without specific prior written permission. 201590Srgrimes * 211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311590Srgrimes * SUCH DAMAGE. 3260833Sjake * 3360833Sjake * $FreeBSD: head/usr.bin/last/last.c 96785 2002-05-17 05:11:07Z jmallett $ 341590Srgrimes */ 351590Srgrimes 361590Srgrimes#ifndef lint 3778201Sddstatic const char copyright[] = 381590Srgrimes"@(#) Copyright (c) 1987, 1993, 1994\n\ 391590Srgrimes The Regents of the University of California. All rights reserved.\n"; 401590Srgrimes#endif /* not lint */ 411590Srgrimes 421590Srgrimes#ifndef lint 4378201Sddstatic const char sccsid[] = "@(#)last.c 8.2 (Berkeley) 4/2/94"; 441590Srgrimes#endif /* not lint */ 451590Srgrimes 461590Srgrimes#include <sys/param.h> 471590Srgrimes#include <sys/stat.h> 481590Srgrimes 491590Srgrimes#include <err.h> 501590Srgrimes#include <fcntl.h> 5174588Sache#include <langinfo.h> 5216438Sache#include <locale.h> 531590Srgrimes#include <paths.h> 541590Srgrimes#include <signal.h> 551590Srgrimes#include <stdio.h> 561590Srgrimes#include <stdlib.h> 571590Srgrimes#include <string.h> 581590Srgrimes#include <time.h> 591590Srgrimes#include <unistd.h> 601590Srgrimes#include <utmp.h> 6111547Sdg#include <sys/queue.h> 621590Srgrimes 631590Srgrimes#define NO 0 /* false/no */ 641590Srgrimes#define YES 1 /* true/yes */ 6577291Sdd#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; 661590Srgrimes 671590Srgrimesstatic struct utmp buf[1024]; /* utmp read buffer */ 681590Srgrimes 691590Srgrimestypedef struct arg { 701590Srgrimes char *name; /* argument */ 711590Srgrimes#define HOST_TYPE -2 721590Srgrimes#define TTY_TYPE -3 731590Srgrimes#define USER_TYPE -4 741590Srgrimes int type; /* type of arg */ 751590Srgrimes struct arg *next; /* linked list pointer */ 761590Srgrimes} ARG; 771590SrgrimesARG *arglist; /* head of linked list */ 781590Srgrimes 7960938SjakeLIST_HEAD(ttylisthead, ttytab) ttylist; 8011547Sdg 8111547Sdgstruct ttytab { 8236062Sjb time_t logout; /* log out time */ 831590Srgrimes char tty[UT_LINESIZE + 1]; /* terminal name */ 8460938Sjake LIST_ENTRY(ttytab) list; 8511547Sdg}; 861590Srgrimes 8791536Siedowsestatic const char *crmsg; /* cause of last reboot */ 881590Srgrimesstatic long currentout, /* current logout value */ 891590Srgrimes maxrec; /* records to display */ 9078201Sddstatic const char *file = _PATH_WTMP; /* wtmp file */ 9136434Sdannystatic int sflag = 0; /* show delta in seconds */ 9236434Sdannystatic int width = 5; /* show seconds in delta */ 9391541Siedowsestatic int yflag; /* show year */ 9474588Sachestatic int d_first; 9591538Siedowsestatic int snapfound = 0; /* found snapshot entry? */ 9677291Sddstatic time_t snaptime; /* if != 0, we will only 9777291Sdd * report users logged in 9877291Sdd * at this snapshot time 9977291Sdd */ 1001590Srgrimes 10192920Simpvoid addarg(int, char *); 10292920Simptime_t dateconv(char *); 10392920Simpvoid doentry(struct utmp *); 10492920Simpvoid hostconv(char *); 10592920Simpvoid onintr(int); 10692920Simpvoid printentry(struct utmp *, struct ttytab *); 10792920Simpchar *ttyconv(char *); 10892920Simpint want(struct utmp *); 10992920Simpvoid usage(void); 11092920Simpvoid wtmp(void); 1111590Srgrimes 11236434Sdannyvoid 11336434Sdannyusage(void) 11436434Sdanny{ 11536434Sdanny (void)fprintf(stderr, 11691541Siedowse"usage: last [-#] [-y] [-d [[CC]YY][MMDD]hhmm[.SS]] [-f file] [-h host]\n" 11781161Sdd"\t[-t tty] [-s|w] [user ...]\n"); 11836434Sdanny exit(1); 11936434Sdanny} 12036434Sdanny 1211590Srgrimesint 1221590Srgrimesmain(argc, argv) 1231590Srgrimes int argc; 1241590Srgrimes char *argv[]; 1251590Srgrimes{ 1261590Srgrimes int ch; 1271590Srgrimes char *p; 1281590Srgrimes 12916438Sache (void) setlocale(LC_TIME, ""); 13074588Sache d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 13116438Sache 1321590Srgrimes maxrec = -1; 13377291Sdd snaptime = 0; 13491541Siedowse while ((ch = getopt(argc, argv, "0123456789d:f:h:st:wy")) != -1) 1351590Srgrimes switch (ch) { 1361590Srgrimes case '0': case '1': case '2': case '3': case '4': 1371590Srgrimes case '5': case '6': case '7': case '8': case '9': 1381590Srgrimes /* 1391590Srgrimes * kludge: last was originally designed to take 1401590Srgrimes * a number after a dash. 1411590Srgrimes */ 1421590Srgrimes if (maxrec == -1) { 1431590Srgrimes p = argv[optind - 1]; 1441590Srgrimes if (p[0] == '-' && p[1] == ch && !p[2]) 1451590Srgrimes maxrec = atol(++p); 1461590Srgrimes else 1471590Srgrimes maxrec = atol(argv[optind] + 1); 1481590Srgrimes if (!maxrec) 1491590Srgrimes exit(0); 1501590Srgrimes } 1511590Srgrimes break; 15277291Sdd case 'd': 15377291Sdd snaptime = dateconv(optarg); 15477291Sdd break; 1551590Srgrimes case 'f': 1561590Srgrimes file = optarg; 1571590Srgrimes break; 1581590Srgrimes case 'h': 1591590Srgrimes hostconv(optarg); 1601590Srgrimes addarg(HOST_TYPE, optarg); 1611590Srgrimes break; 16236434Sdanny case 's': 16336434Sdanny sflag++; /* Show delta as seconds */ 16436434Sdanny break; 1651590Srgrimes case 't': 1661590Srgrimes addarg(TTY_TYPE, ttyconv(optarg)); 1671590Srgrimes break; 16836434Sdanny case 'w': 16936434Sdanny width = 8; 17036434Sdanny break; 17191541Siedowse case 'y': 17291541Siedowse yflag++; 17391541Siedowse break; 1741590Srgrimes case '?': 1751590Srgrimes default: 17636434Sdanny usage(); 1771590Srgrimes } 1781590Srgrimes 17936434Sdanny if (sflag && width == 8) usage(); 18036434Sdanny 1811590Srgrimes if (argc) { 1821590Srgrimes setlinebuf(stdout); 1831590Srgrimes for (argv += optind; *argv; ++argv) { 1841590Srgrimes#define COMPATIBILITY 1851590Srgrimes#ifdef COMPATIBILITY 1861590Srgrimes /* code to allow "last p5" to work */ 1871590Srgrimes addarg(TTY_TYPE, ttyconv(*argv)); 1881590Srgrimes#endif 1891590Srgrimes addarg(USER_TYPE, *argv); 1901590Srgrimes } 1911590Srgrimes } 1921590Srgrimes wtmp(); 1931590Srgrimes exit(0); 1941590Srgrimes} 1951590Srgrimes 1961590Srgrimes/* 1971590Srgrimes * wtmp -- 1981590Srgrimes * read through the wtmp file 1991590Srgrimes */ 2001590Srgrimesvoid 2011590Srgrimeswtmp() 2021590Srgrimes{ 2031590Srgrimes struct utmp *bp; /* current structure */ 2041590Srgrimes struct stat stb; /* stat of file for size */ 20536062Sjb long bl; 2061590Srgrimes int bytes, wfd; 20716438Sache char ct[80]; 20816438Sache struct tm *tm; 20985648Sdillon time_t t; 2101590Srgrimes 21111547Sdg LIST_INIT(&ttylist); 21211547Sdg 2131590Srgrimes if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1) 2141590Srgrimes err(1, "%s", file); 2151590Srgrimes bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf); 2161590Srgrimes 21785648Sdillon (void)time(&t); 21889572Sdillon buf[0].ut_time = _time_to_int(t); 2191590Srgrimes (void)signal(SIGINT, onintr); 2201590Srgrimes (void)signal(SIGQUIT, onintr); 2211590Srgrimes 2221590Srgrimes while (--bl >= 0) { 2231590Srgrimes if (lseek(wfd, (off_t)(bl * sizeof(buf)), L_SET) == -1 || 2241590Srgrimes (bytes = read(wfd, buf, sizeof(buf))) == -1) 2251590Srgrimes err(1, "%s", file); 22691536Siedowse for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) 22791536Siedowse doentry(bp); 2281590Srgrimes } 22989572Sdillon t = _int_to_time(buf[0].ut_time); 23085648Sdillon tm = localtime(&t); 23178201Sdd (void) strftime(ct, sizeof(ct), "\nwtmp begins %+\n", tm); 23262871Skris printf("%s", ct); 2331590Srgrimes} 2341590Srgrimes 2351590Srgrimes/* 23691536Siedowse * doentry -- 23791536Siedowse * process a single wtmp entry 23891536Siedowse */ 23991536Siedowsevoid 24091536Siedowsedoentry(bp) 24191536Siedowse struct utmp *bp; 24291536Siedowse{ 24391536Siedowse struct ttytab *tt, *ttx; /* ttylist entry */ 24491536Siedowse 24591536Siedowse /* 24691536Siedowse * if the terminal line is '~', the machine stopped. 24791536Siedowse * see utmp(5) for more info. 24891536Siedowse */ 24991536Siedowse if (bp->ut_line[0] == '~' && !bp->ut_line[1]) { 25091536Siedowse /* everybody just logged out */ 25191536Siedowse for (tt = LIST_FIRST(&ttylist); tt;) { 25291536Siedowse LIST_REMOVE(tt, list); 25391536Siedowse ttx = tt; 25491536Siedowse tt = LIST_NEXT(tt, list); 25591536Siedowse free(ttx); 25691536Siedowse } 25791536Siedowse currentout = -bp->ut_time; 25891536Siedowse crmsg = strncmp(bp->ut_name, "shutdown", UT_NAMESIZE) ? 25991536Siedowse "crash" : "shutdown"; 26091536Siedowse /* 26191536Siedowse * if we're in snapshot mode, we want to exit if this 26291536Siedowse * shutdown/reboot appears while we we are tracking the 26391536Siedowse * active range 26491536Siedowse */ 26591536Siedowse if (snaptime && snapfound) 26691536Siedowse exit(0); 26791536Siedowse /* 26891536Siedowse * don't print shutdown/reboot entries unless flagged for 26991536Siedowse */ 27091538Siedowse if (!snaptime && want(bp)) 27191536Siedowse printentry(bp, NULL); 27291536Siedowse return; 27391536Siedowse } 27491536Siedowse /* 27591536Siedowse * if the line is '{' or '|', date got set; see 27691536Siedowse * utmp(5) for more info. 27791536Siedowse */ 27891536Siedowse if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|') && 27991536Siedowse !bp->ut_line[1]) { 28091538Siedowse if (want(bp) && !snaptime) 28191536Siedowse printentry(bp, NULL); 28291536Siedowse return; 28391536Siedowse } 28491536Siedowse /* find associated tty */ 28591536Siedowse LIST_FOREACH(tt, &ttylist, list) 28691536Siedowse if (!strncmp(tt->tty, bp->ut_line, UT_LINESIZE)) 28791536Siedowse break; 28891536Siedowse 28991536Siedowse if (tt == NULL) { 29091536Siedowse /* add new one */ 29191536Siedowse tt = malloc(sizeof(struct ttytab)); 29291536Siedowse if (tt == NULL) 29396785Sjmallett errx(1, "malloc failure"); 29491536Siedowse tt->logout = currentout; 29591536Siedowse strncpy(tt->tty, bp->ut_line, UT_LINESIZE); 29691536Siedowse LIST_INSERT_HEAD(&ttylist, tt, list); 29791536Siedowse } 29891536Siedowse 29991536Siedowse /* 30091536Siedowse * print record if not in snapshot mode and wanted 30191536Siedowse * or in snapshot mode and in snapshot range 30291536Siedowse */ 30391536Siedowse if (bp->ut_name[0] && (want(bp) || (bp->ut_time < snaptime && 30491536Siedowse (tt->logout > snaptime || tt->logout < 1)))) { 30591536Siedowse snapfound = 1; 30691536Siedowse /* 30791536Siedowse * when uucp and ftp log in over a network, the entry in 30891536Siedowse * the utmp file is the name plus their process id. See 30991536Siedowse * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information. 31091536Siedowse */ 31191536Siedowse if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1)) 31291536Siedowse bp->ut_line[3] = '\0'; 31391536Siedowse else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1)) 31491536Siedowse bp->ut_line[4] = '\0'; 31591536Siedowse printentry(bp, tt); 31691536Siedowse } 31791536Siedowse tt->logout = bp->ut_time; 31891536Siedowse} 31991536Siedowse 32091536Siedowse/* 32191536Siedowse * printentry -- 32291536Siedowse * output an entry 32391536Siedowse * 32491536Siedowse * If `tt' is non-NULL, use it and `crmsg' to print the logout time or 32591536Siedowse * logout type (crash/shutdown) as appropriate. 32691536Siedowse */ 32791536Siedowsevoid 32891536Siedowseprintentry(bp, tt) 32991536Siedowse struct utmp *bp; 33091536Siedowse struct ttytab *tt; 33191536Siedowse{ 33291536Siedowse char ct[80]; 33391536Siedowse struct tm *tm; 33491536Siedowse time_t delta; /* time difference */ 33591536Siedowse time_t t; 33691536Siedowse 33791538Siedowse if (maxrec != -1 && !maxrec--) 33891538Siedowse exit(0); 33991536Siedowse t = _int_to_time(bp->ut_time); 34091536Siedowse tm = localtime(&t); 34191541Siedowse (void) strftime(ct, sizeof(ct), d_first ? 34291541Siedowse (yflag ? "%a %e %b %Y %R" : "%a %e %b %R") : 34391541Siedowse (yflag ? "%a %b %e %Y %R" : "%a %b %e %R"), tm); 34491536Siedowse printf("%-*.*s %-*.*s %-*.*s %s%c", 34591536Siedowse UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, 34691536Siedowse UT_LINESIZE, UT_LINESIZE, bp->ut_line, 34791536Siedowse UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, 34891536Siedowse ct, tt == NULL ? '\n' : ' '); 34991536Siedowse if (tt == NULL) 35091536Siedowse return; 35191536Siedowse if (!tt->logout) { 35291536Siedowse puts(" still logged in"); 35391536Siedowse return; 35491536Siedowse } 35591536Siedowse if (tt->logout < 0) { 35691536Siedowse tt->logout = -tt->logout; 35791536Siedowse printf("- %s", crmsg); 35891536Siedowse } else { 35991536Siedowse tm = localtime(&tt->logout); 36091536Siedowse (void) strftime(ct, sizeof(ct), "%R", tm); 36191536Siedowse printf("- %s", ct); 36291536Siedowse } 36391536Siedowse delta = tt->logout - bp->ut_time; 36491536Siedowse if (sflag) { 36591536Siedowse printf(" (%8ld)\n", (long)delta); 36691536Siedowse } else { 36791536Siedowse tm = gmtime(&delta); 36891536Siedowse (void) strftime(ct, sizeof(ct), width >= 8 ? "%T" : "%R", tm); 36991536Siedowse if (delta < 86400) 37091536Siedowse printf(" (%s)\n", ct); 37191536Siedowse else 37291536Siedowse printf(" (%ld+%s)\n", (long)delta / 86400, ct); 37391536Siedowse } 37491536Siedowse} 37591536Siedowse 37691536Siedowse/* 3771590Srgrimes * want -- 3781590Srgrimes * see if want this entry 3791590Srgrimes */ 3801590Srgrimesint 38111547Sdgwant(bp) 3821590Srgrimes struct utmp *bp; 3831590Srgrimes{ 3841590Srgrimes ARG *step; 3851590Srgrimes 38677291Sdd if (snaptime) 38777291Sdd return (NO); 38877291Sdd 3891590Srgrimes if (!arglist) 3901590Srgrimes return (YES); 3911590Srgrimes 3921590Srgrimes for (step = arglist; step; step = step->next) 3931590Srgrimes switch(step->type) { 3941590Srgrimes case HOST_TYPE: 3951590Srgrimes if (!strncasecmp(step->name, bp->ut_host, UT_HOSTSIZE)) 3961590Srgrimes return (YES); 3971590Srgrimes break; 3981590Srgrimes case TTY_TYPE: 3991590Srgrimes if (!strncmp(step->name, bp->ut_line, UT_LINESIZE)) 4001590Srgrimes return (YES); 4011590Srgrimes break; 4021590Srgrimes case USER_TYPE: 4031590Srgrimes if (!strncmp(step->name, bp->ut_name, UT_NAMESIZE)) 4041590Srgrimes return (YES); 4051590Srgrimes break; 40691536Siedowse } 4071590Srgrimes return (NO); 4081590Srgrimes} 4091590Srgrimes 4101590Srgrimes/* 4111590Srgrimes * addarg -- 4121590Srgrimes * add an entry to a linked list of arguments 4131590Srgrimes */ 4141590Srgrimesvoid 4151590Srgrimesaddarg(type, arg) 4161590Srgrimes int type; 4171590Srgrimes char *arg; 4181590Srgrimes{ 4191590Srgrimes ARG *cur; 4201590Srgrimes 42196785Sjmallett if ((cur = malloc(sizeof(ARG))) == NULL) 42296785Sjmallett errx(1, "malloc failure"); 4231590Srgrimes cur->next = arglist; 4241590Srgrimes cur->type = type; 4251590Srgrimes cur->name = arg; 4261590Srgrimes arglist = cur; 4271590Srgrimes} 4281590Srgrimes 4291590Srgrimes/* 4301590Srgrimes * hostconv -- 4311590Srgrimes * convert the hostname to search pattern; if the supplied host name 4321590Srgrimes * has a domain attached that is the same as the current domain, rip 4331590Srgrimes * off the domain suffix since that's what login(1) does. 4341590Srgrimes */ 4351590Srgrimesvoid 4361590Srgrimeshostconv(arg) 4371590Srgrimes char *arg; 4381590Srgrimes{ 4391590Srgrimes static int first = 1; 4401590Srgrimes static char *hostdot, name[MAXHOSTNAMELEN]; 4411590Srgrimes char *argdot; 4421590Srgrimes 4431590Srgrimes if (!(argdot = strchr(arg, '.'))) 4441590Srgrimes return; 4451590Srgrimes if (first) { 4461590Srgrimes first = 0; 4471590Srgrimes if (gethostname(name, sizeof(name))) 4481590Srgrimes err(1, "gethostname"); 4491590Srgrimes hostdot = strchr(name, '.'); 4501590Srgrimes } 4511590Srgrimes if (hostdot && !strcasecmp(hostdot, argdot)) 4521590Srgrimes *argdot = '\0'; 4531590Srgrimes} 4541590Srgrimes 4551590Srgrimes/* 4561590Srgrimes * ttyconv -- 4571590Srgrimes * convert tty to correct name. 4581590Srgrimes */ 4591590Srgrimeschar * 4601590Srgrimesttyconv(arg) 4611590Srgrimes char *arg; 4621590Srgrimes{ 4631590Srgrimes char *mval; 4641590Srgrimes 4651590Srgrimes /* 4661590Srgrimes * kludge -- we assume that all tty's end with 4671590Srgrimes * a two character suffix. 4681590Srgrimes */ 4691590Srgrimes if (strlen(arg) == 2) { 4701590Srgrimes /* either 6 for "ttyxx" or 8 for "console" */ 47196785Sjmallett if ((mval = malloc(8)) == NULL) 47296785Sjmallett errx(1, "malloc failure"); 4731590Srgrimes if (!strcmp(arg, "co")) 4741590Srgrimes (void)strcpy(mval, "console"); 4751590Srgrimes else { 4761590Srgrimes (void)strcpy(mval, "tty"); 4771590Srgrimes (void)strcpy(mval + 3, arg); 4781590Srgrimes } 4791590Srgrimes return (mval); 4801590Srgrimes } 4811590Srgrimes if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1)) 4821590Srgrimes return (arg + 5); 4831590Srgrimes return (arg); 4841590Srgrimes} 4851590Srgrimes 4861590Srgrimes/* 48777291Sdd * dateconv -- 48877291Sdd * Convert the snapshot time in command line given in the format 48977291Sdd * [[CC]YY]MMDDhhmm[.SS]] to a time_t. 49077291Sdd * Derived from atime_arg1() in usr.bin/touch/touch.c 49177291Sdd */ 49277291Sddtime_t 49377291Sdddateconv(arg) 49477291Sdd char *arg; 49577291Sdd{ 49677291Sdd time_t timet; 49777291Sdd struct tm *t; 49877291Sdd int yearset; 49977291Sdd char *p; 50077291Sdd 50177291Sdd /* Start with the current time. */ 50277291Sdd if (time(&timet) < 0) 50377291Sdd err(1, "time"); 50477291Sdd if ((t = localtime(&timet)) == NULL) 50577291Sdd err(1, "localtime"); 50677291Sdd 50777291Sdd /* [[CC]YY]MMDDhhmm[.SS] */ 50877291Sdd if ((p = strchr(arg, '.')) == NULL) 50977291Sdd t->tm_sec = 0; /* Seconds defaults to 0. */ 51077291Sdd else { 51177291Sdd if (strlen(p + 1) != 2) 51277291Sdd goto terr; 51377291Sdd *p++ = '\0'; 51477291Sdd t->tm_sec = ATOI2(p); 51577291Sdd } 51677291Sdd 51777291Sdd yearset = 0; 51877291Sdd switch (strlen(arg)) { 51977291Sdd case 12: /* CCYYMMDDhhmm */ 52077291Sdd t->tm_year = ATOI2(arg); 52177291Sdd t->tm_year *= 100; 52277291Sdd yearset = 1; 52377291Sdd /* FALLTHOUGH */ 52477291Sdd case 10: /* YYMMDDhhmm */ 52577291Sdd if (yearset) { 52677291Sdd yearset = ATOI2(arg); 52777291Sdd t->tm_year += yearset; 52877291Sdd } else { 52977291Sdd yearset = ATOI2(arg); 53077291Sdd if (yearset < 69) 53177291Sdd t->tm_year = yearset + 2000; 53277291Sdd else 53377291Sdd t->tm_year = yearset + 1900; 53477291Sdd } 53577291Sdd t->tm_year -= 1900; /* Convert to UNIX time. */ 53677291Sdd /* FALLTHROUGH */ 53777291Sdd case 8: /* MMDDhhmm */ 53877291Sdd t->tm_mon = ATOI2(arg); 53977291Sdd --t->tm_mon; /* Convert from 01-12 to 00-11 */ 54077291Sdd t->tm_mday = ATOI2(arg); 54177291Sdd t->tm_hour = ATOI2(arg); 54277291Sdd t->tm_min = ATOI2(arg); 54377291Sdd break; 54477291Sdd case 4: /* hhmm */ 54577291Sdd t->tm_hour = ATOI2(arg); 54677291Sdd t->tm_min = ATOI2(arg); 54777291Sdd break; 54877291Sdd default: 54977291Sdd goto terr; 55077291Sdd } 55177291Sdd t->tm_isdst = -1; /* Figure out DST. */ 55277291Sdd timet = mktime(t); 55377291Sdd if (timet == -1) 55477291Sddterr: errx(1, 55577291Sdd "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); 55677291Sdd return timet; 55777291Sdd} 55877291Sdd 55977291Sdd 56077291Sdd/* 5611590Srgrimes * onintr -- 5621590Srgrimes * on interrupt, we inform the user how far we've gotten 5631590Srgrimes */ 5641590Srgrimesvoid 5651590Srgrimesonintr(signo) 5661590Srgrimes int signo; 5671590Srgrimes{ 56816438Sache char ct[80]; 56916438Sache struct tm *tm; 57089572Sdillon time_t t = _int_to_time(buf[0].ut_time); 5711590Srgrimes 57285648Sdillon tm = localtime(&t); 57374588Sache (void) strftime(ct, sizeof(ct), 57474588Sache d_first ? "%a %e %b %R" : "%a %b %e %R", 57574588Sache tm); 57674588Sache printf("\ninterrupted %s\n", ct); 5771590Srgrimes if (signo == SIGINT) 5781590Srgrimes exit(1); 5791590Srgrimes (void)fflush(stdout); /* fix required for rsh */ 5801590Srgrimes} 581