last.c revision 78201
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 78201 2001-06-14 04:55:26Z dd $ 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 871590Srgrimesstatic long currentout, /* current logout value */ 881590Srgrimes maxrec; /* records to display */ 8978201Sddstatic const char *file = _PATH_WTMP; /* wtmp file */ 9036434Sdannystatic int sflag = 0; /* show delta in seconds */ 9136434Sdannystatic int width = 5; /* show seconds in delta */ 9274588Sachestatic int d_first; 9377291Sddstatic time_t snaptime; /* if != 0, we will only 9477291Sdd * report users logged in 9577291Sdd * at this snapshot time 9677291Sdd */ 971590Srgrimes 981590Srgrimesvoid addarg __P((int, char *)); 9978201Sddtime_t dateconv __P((char *)); 1001590Srgrimesvoid hostconv __P((char *)); 1011590Srgrimesvoid onintr __P((int)); 1021590Srgrimeschar *ttyconv __P((char *)); 10311547Sdgint want __P((struct utmp *)); 10478201Sddvoid usage __P((void)); 1051590Srgrimesvoid wtmp __P((void)); 1061590Srgrimes 10736434Sdannyvoid 10836434Sdannyusage(void) 10936434Sdanny{ 11036434Sdanny (void)fprintf(stderr, 11136434Sdanny "usage: last [-#] [-f file] [-h hostname] [-t tty] [-s|w] [user ...]\n"); 11236434Sdanny exit(1); 11336434Sdanny} 11436434Sdanny 1151590Srgrimesint 1161590Srgrimesmain(argc, argv) 1171590Srgrimes int argc; 1181590Srgrimes char *argv[]; 1191590Srgrimes{ 1201590Srgrimes int ch; 1211590Srgrimes char *p; 1221590Srgrimes 12316438Sache (void) setlocale(LC_TIME, ""); 12474588Sache d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 12516438Sache 1261590Srgrimes maxrec = -1; 12777291Sdd snaptime = 0; 12877291Sdd while ((ch = getopt(argc, argv, "0123456789d:f:h:st:w")) != -1) 1291590Srgrimes switch (ch) { 1301590Srgrimes case '0': case '1': case '2': case '3': case '4': 1311590Srgrimes case '5': case '6': case '7': case '8': case '9': 1321590Srgrimes /* 1331590Srgrimes * kludge: last was originally designed to take 1341590Srgrimes * a number after a dash. 1351590Srgrimes */ 1361590Srgrimes if (maxrec == -1) { 1371590Srgrimes p = argv[optind - 1]; 1381590Srgrimes if (p[0] == '-' && p[1] == ch && !p[2]) 1391590Srgrimes maxrec = atol(++p); 1401590Srgrimes else 1411590Srgrimes maxrec = atol(argv[optind] + 1); 1421590Srgrimes if (!maxrec) 1431590Srgrimes exit(0); 1441590Srgrimes } 1451590Srgrimes break; 14677291Sdd case 'd': 14777291Sdd snaptime = dateconv(optarg); 14877291Sdd break; 1491590Srgrimes case 'f': 1501590Srgrimes file = optarg; 1511590Srgrimes break; 1521590Srgrimes case 'h': 1531590Srgrimes hostconv(optarg); 1541590Srgrimes addarg(HOST_TYPE, optarg); 1551590Srgrimes break; 15636434Sdanny case 's': 15736434Sdanny sflag++; /* Show delta as seconds */ 15836434Sdanny break; 1591590Srgrimes case 't': 1601590Srgrimes addarg(TTY_TYPE, ttyconv(optarg)); 1611590Srgrimes break; 16236434Sdanny case 'w': 16336434Sdanny width = 8; 16436434Sdanny break; 1651590Srgrimes case '?': 1661590Srgrimes default: 16736434Sdanny usage(); 1681590Srgrimes } 1691590Srgrimes 17036434Sdanny if (sflag && width == 8) usage(); 17136434Sdanny 1721590Srgrimes if (argc) { 1731590Srgrimes setlinebuf(stdout); 1741590Srgrimes for (argv += optind; *argv; ++argv) { 1751590Srgrimes#define COMPATIBILITY 1761590Srgrimes#ifdef COMPATIBILITY 1771590Srgrimes /* code to allow "last p5" to work */ 1781590Srgrimes addarg(TTY_TYPE, ttyconv(*argv)); 1791590Srgrimes#endif 1801590Srgrimes addarg(USER_TYPE, *argv); 1811590Srgrimes } 1821590Srgrimes } 1831590Srgrimes wtmp(); 1841590Srgrimes exit(0); 1851590Srgrimes} 1861590Srgrimes 1871590Srgrimes/* 1881590Srgrimes * wtmp -- 1891590Srgrimes * read through the wtmp file 1901590Srgrimes */ 1911590Srgrimesvoid 1921590Srgrimeswtmp() 1931590Srgrimes{ 1941590Srgrimes struct utmp *bp; /* current structure */ 19519223Sjoerg struct ttytab *tt, *ttx; /* ttylist entry */ 1961590Srgrimes struct stat stb; /* stat of file for size */ 19736062Sjb long bl; 19836062Sjb time_t delta; /* time difference */ 1991590Srgrimes int bytes, wfd; 20078201Sdd const char *crmsg; 20116438Sache char ct[80]; 20216438Sache struct tm *tm; 20377291Sdd int snapfound = 0; /* found snapshot entry? */ 2041590Srgrimes 20511547Sdg LIST_INIT(&ttylist); 20611547Sdg 2071590Srgrimes if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1) 2081590Srgrimes err(1, "%s", file); 2091590Srgrimes bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf); 2101590Srgrimes 2111590Srgrimes (void)time(&buf[0].ut_time); 2121590Srgrimes (void)signal(SIGINT, onintr); 2131590Srgrimes (void)signal(SIGQUIT, onintr); 2141590Srgrimes 2151590Srgrimes while (--bl >= 0) { 2161590Srgrimes if (lseek(wfd, (off_t)(bl * sizeof(buf)), L_SET) == -1 || 2171590Srgrimes (bytes = read(wfd, buf, sizeof(buf))) == -1) 2181590Srgrimes err(1, "%s", file); 2191590Srgrimes for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) { 2201590Srgrimes /* 2211590Srgrimes * if the terminal line is '~', the machine stopped. 2221590Srgrimes * see utmp(5) for more info. 2231590Srgrimes */ 2241590Srgrimes if (bp->ut_line[0] == '~' && !bp->ut_line[1]) { 2251590Srgrimes /* everybody just logged out */ 22670467Sphk for (tt = LIST_FIRST(&ttylist); tt;) { 22711547Sdg LIST_REMOVE(tt, list); 22819223Sjoerg ttx = tt; 22970467Sphk tt = LIST_NEXT(tt, list); 23019223Sjoerg free(ttx); 23111547Sdg } 2321590Srgrimes currentout = -bp->ut_time; 2331590Srgrimes crmsg = strncmp(bp->ut_name, "shutdown", 2341590Srgrimes UT_NAMESIZE) ? "crash" : "shutdown"; 23577291Sdd /* 23677291Sdd * if we're in snapshot mode, we want to 23777291Sdd * exit if this shutdown/reboot appears 23877291Sdd * while we we are tracking the active 23977291Sdd * range 24077291Sdd */ 24177291Sdd if (snaptime && snapfound) 24277291Sdd return; 24377291Sdd /* 24477291Sdd * don't print shutdown/reboot entries 24577291Sdd * unless flagged for 24677291Sdd */ 24777291Sdd if (!snaptime && want(bp)) { 24816438Sache tm = localtime(&bp->ut_time); 24974588Sache (void) strftime(ct, sizeof(ct), 25074588Sache d_first ? "%a %e %b %R" : 25174588Sache "%a %b %e %R", 25274588Sache tm); 25374588Sache printf("%-*.*s %-*.*s %-*.*s %s\n", 2541590Srgrimes UT_NAMESIZE, UT_NAMESIZE, 2551590Srgrimes bp->ut_name, UT_LINESIZE, 2561590Srgrimes UT_LINESIZE, bp->ut_line, 2571590Srgrimes UT_HOSTSIZE, UT_HOSTSIZE, 25874588Sache bp->ut_host, ct); 2591590Srgrimes if (maxrec != -1 && !--maxrec) 2601590Srgrimes return; 2611590Srgrimes } 2621590Srgrimes continue; 2631590Srgrimes } 2641590Srgrimes /* 2651590Srgrimes * if the line is '{' or '|', date got set; see 2661590Srgrimes * utmp(5) for more info. 2671590Srgrimes */ 2681590Srgrimes if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|') 2691590Srgrimes && !bp->ut_line[1]) { 27077291Sdd if (want(bp) && !snaptime) { 27116438Sache tm = localtime(&bp->ut_time); 27274588Sache (void) strftime(ct, sizeof(ct), 27374588Sache d_first ? "%a %e %b %R" : 27474588Sache "%a %b %e %R", 27574588Sache tm); 27674588Sache printf("%-*.*s %-*.*s %-*.*s %s\n", 27711547Sdg UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, 27811547Sdg UT_LINESIZE, UT_LINESIZE, bp->ut_line, 27911547Sdg UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, 28074588Sache ct); 2811590Srgrimes if (maxrec && !--maxrec) 2821590Srgrimes return; 2831590Srgrimes } 2841590Srgrimes continue; 2851590Srgrimes } 28677291Sdd /* find associated tty */ 28777291Sdd LIST_FOREACH(tt, &ttylist, list) 28877291Sdd if (!strncmp(tt->tty, bp->ut_line, UT_LINESIZE)) 28977291Sdd break; 29077291Sdd 29177291Sdd if (tt == NULL) { 29277291Sdd /* add new one */ 29377291Sdd tt = malloc(sizeof(struct ttytab)); 29477291Sdd if (tt == NULL) 29577291Sdd err(1, "malloc failure"); 29677291Sdd tt->logout = currentout; 29777291Sdd strncpy(tt->tty, bp->ut_line, UT_LINESIZE); 29877291Sdd LIST_INSERT_HEAD(&ttylist, tt, list); 29977291Sdd } 30077291Sdd 30177291Sdd /* 30277291Sdd * print record if not in snapshot mode and wanted 30377291Sdd * or in snapshot mode and in snapshot range 30477291Sdd */ 30577291Sdd if (bp->ut_name[0] && (want(bp) || 30677291Sdd (bp->ut_time < snaptime && 30777291Sdd (tt->logout > snaptime || tt->logout < 1)))) { 30877291Sdd snapfound = 1; 30977291Sdd /* 31077291Sdd * when uucp and ftp log in over a network, the entry in 31177291Sdd * the utmp file is the name plus their process id. See 31277291Sdd * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information. 31377291Sdd */ 31477291Sdd if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1)) 31577291Sdd bp->ut_line[3] = '\0'; 31677291Sdd else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1)) 31777291Sdd bp->ut_line[4] = '\0'; 31877291Sdd tm = localtime(&bp->ut_time); 31977291Sdd (void) strftime(ct, sizeof(ct), 32077291Sdd d_first ? "%a %e %b %R" : 32177291Sdd "%a %b %e %R", 32277291Sdd tm); 32377291Sdd printf("%-*.*s %-*.*s %-*.*s %s ", 32477291Sdd UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, 32577291Sdd UT_LINESIZE, UT_LINESIZE, bp->ut_line, 32677291Sdd UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, 32777291Sdd ct); 32877291Sdd if (!tt->logout) 32977291Sdd puts(" still logged in"); 33077291Sdd else { 33177291Sdd if (tt->logout < 0) { 33277291Sdd tt->logout = -tt->logout; 33377291Sdd printf("- %s", crmsg); 33477291Sdd } 33511547Sdg else { 33677291Sdd tm = localtime(&tt->logout); 33777291Sdd (void) strftime(ct, sizeof(ct), "%R", tm); 33877291Sdd printf("- %s", ct); 33977291Sdd } 34077291Sdd delta = tt->logout - bp->ut_time; 34177291Sdd if ( sflag ) { 34278201Sdd printf(" (%8ld)\n", 34378201Sdd (long)delta); 34477291Sdd } else { 34577291Sdd tm = gmtime(&delta); 34677291Sdd (void) strftime(ct, sizeof(ct), 34777291Sdd width >= 8 ? "%T" : "%R", 34877291Sdd tm); 34977291Sdd if (delta < 86400) 35074588Sache printf(" (%s)\n", ct); 35177291Sdd else 35274588Sache printf(" (%ld+%s)\n", 35378201Sdd (long)delta / 35478201Sdd 86400, ct); 3551590Srgrimes } 3561590Srgrimes } 35777291Sdd if (maxrec != -1 && !--maxrec) 35877291Sdd return; 3591590Srgrimes } 36077328Sru tt->logout = bp->ut_time; 3611590Srgrimes } 3621590Srgrimes } 36316438Sache tm = localtime(&buf[0].ut_time); 36478201Sdd (void) strftime(ct, sizeof(ct), "\nwtmp begins %+\n", tm); 36562871Skris printf("%s", ct); 3661590Srgrimes} 3671590Srgrimes 3681590Srgrimes/* 3691590Srgrimes * want -- 3701590Srgrimes * see if want this entry 3711590Srgrimes */ 3721590Srgrimesint 37311547Sdgwant(bp) 3741590Srgrimes struct utmp *bp; 3751590Srgrimes{ 3761590Srgrimes ARG *step; 3771590Srgrimes 37877291Sdd if (snaptime) 37977291Sdd return (NO); 38077291Sdd 3811590Srgrimes if (!arglist) 3821590Srgrimes return (YES); 3831590Srgrimes 3841590Srgrimes for (step = arglist; step; step = step->next) 3851590Srgrimes switch(step->type) { 3861590Srgrimes case HOST_TYPE: 3871590Srgrimes if (!strncasecmp(step->name, bp->ut_host, UT_HOSTSIZE)) 3881590Srgrimes return (YES); 3891590Srgrimes break; 3901590Srgrimes case TTY_TYPE: 3911590Srgrimes if (!strncmp(step->name, bp->ut_line, UT_LINESIZE)) 3921590Srgrimes return (YES); 3931590Srgrimes break; 3941590Srgrimes case USER_TYPE: 3951590Srgrimes if (!strncmp(step->name, bp->ut_name, UT_NAMESIZE)) 3961590Srgrimes return (YES); 3971590Srgrimes break; 3981590Srgrimes } 3991590Srgrimes return (NO); 4001590Srgrimes} 4011590Srgrimes 4021590Srgrimes/* 4031590Srgrimes * addarg -- 4041590Srgrimes * add an entry to a linked list of arguments 4051590Srgrimes */ 4061590Srgrimesvoid 4071590Srgrimesaddarg(type, arg) 4081590Srgrimes int type; 4091590Srgrimes char *arg; 4101590Srgrimes{ 4111590Srgrimes ARG *cur; 4121590Srgrimes 4131590Srgrimes if (!(cur = (ARG *)malloc((u_int)sizeof(ARG)))) 4141590Srgrimes err(1, "malloc failure"); 4151590Srgrimes cur->next = arglist; 4161590Srgrimes cur->type = type; 4171590Srgrimes cur->name = arg; 4181590Srgrimes arglist = cur; 4191590Srgrimes} 4201590Srgrimes 4211590Srgrimes/* 4221590Srgrimes * hostconv -- 4231590Srgrimes * convert the hostname to search pattern; if the supplied host name 4241590Srgrimes * has a domain attached that is the same as the current domain, rip 4251590Srgrimes * off the domain suffix since that's what login(1) does. 4261590Srgrimes */ 4271590Srgrimesvoid 4281590Srgrimeshostconv(arg) 4291590Srgrimes char *arg; 4301590Srgrimes{ 4311590Srgrimes static int first = 1; 4321590Srgrimes static char *hostdot, name[MAXHOSTNAMELEN]; 4331590Srgrimes char *argdot; 4341590Srgrimes 4351590Srgrimes if (!(argdot = strchr(arg, '.'))) 4361590Srgrimes return; 4371590Srgrimes if (first) { 4381590Srgrimes first = 0; 4391590Srgrimes if (gethostname(name, sizeof(name))) 4401590Srgrimes err(1, "gethostname"); 4411590Srgrimes hostdot = strchr(name, '.'); 4421590Srgrimes } 4431590Srgrimes if (hostdot && !strcasecmp(hostdot, argdot)) 4441590Srgrimes *argdot = '\0'; 4451590Srgrimes} 4461590Srgrimes 4471590Srgrimes/* 4481590Srgrimes * ttyconv -- 4491590Srgrimes * convert tty to correct name. 4501590Srgrimes */ 4511590Srgrimeschar * 4521590Srgrimesttyconv(arg) 4531590Srgrimes char *arg; 4541590Srgrimes{ 4551590Srgrimes char *mval; 4561590Srgrimes 4571590Srgrimes /* 4581590Srgrimes * kludge -- we assume that all tty's end with 4591590Srgrimes * a two character suffix. 4601590Srgrimes */ 4611590Srgrimes if (strlen(arg) == 2) { 4621590Srgrimes /* either 6 for "ttyxx" or 8 for "console" */ 4631590Srgrimes if (!(mval = malloc((u_int)8))) 4641590Srgrimes err(1, "malloc failure"); 4651590Srgrimes if (!strcmp(arg, "co")) 4661590Srgrimes (void)strcpy(mval, "console"); 4671590Srgrimes else { 4681590Srgrimes (void)strcpy(mval, "tty"); 4691590Srgrimes (void)strcpy(mval + 3, arg); 4701590Srgrimes } 4711590Srgrimes return (mval); 4721590Srgrimes } 4731590Srgrimes if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1)) 4741590Srgrimes return (arg + 5); 4751590Srgrimes return (arg); 4761590Srgrimes} 4771590Srgrimes 4781590Srgrimes/* 47977291Sdd * dateconv -- 48077291Sdd * Convert the snapshot time in command line given in the format 48177291Sdd * [[CC]YY]MMDDhhmm[.SS]] to a time_t. 48277291Sdd * Derived from atime_arg1() in usr.bin/touch/touch.c 48377291Sdd */ 48477291Sddtime_t 48577291Sdddateconv(arg) 48677291Sdd char *arg; 48777291Sdd{ 48877291Sdd time_t timet; 48977291Sdd struct tm *t; 49077291Sdd int yearset; 49177291Sdd char *p; 49277291Sdd 49377291Sdd /* Start with the current time. */ 49477291Sdd if (time(&timet) < 0) 49577291Sdd err(1, "time"); 49677291Sdd if ((t = localtime(&timet)) == NULL) 49777291Sdd err(1, "localtime"); 49877291Sdd 49977291Sdd /* [[CC]YY]MMDDhhmm[.SS] */ 50077291Sdd if ((p = strchr(arg, '.')) == NULL) 50177291Sdd t->tm_sec = 0; /* Seconds defaults to 0. */ 50277291Sdd else { 50377291Sdd if (strlen(p + 1) != 2) 50477291Sdd goto terr; 50577291Sdd *p++ = '\0'; 50677291Sdd t->tm_sec = ATOI2(p); 50777291Sdd } 50877291Sdd 50977291Sdd yearset = 0; 51077291Sdd switch (strlen(arg)) { 51177291Sdd case 12: /* CCYYMMDDhhmm */ 51277291Sdd t->tm_year = ATOI2(arg); 51377291Sdd t->tm_year *= 100; 51477291Sdd yearset = 1; 51577291Sdd /* FALLTHOUGH */ 51677291Sdd case 10: /* YYMMDDhhmm */ 51777291Sdd if (yearset) { 51877291Sdd yearset = ATOI2(arg); 51977291Sdd t->tm_year += yearset; 52077291Sdd } else { 52177291Sdd yearset = ATOI2(arg); 52277291Sdd if (yearset < 69) 52377291Sdd t->tm_year = yearset + 2000; 52477291Sdd else 52577291Sdd t->tm_year = yearset + 1900; 52677291Sdd } 52777291Sdd t->tm_year -= 1900; /* Convert to UNIX time. */ 52877291Sdd /* FALLTHROUGH */ 52977291Sdd case 8: /* MMDDhhmm */ 53077291Sdd t->tm_mon = ATOI2(arg); 53177291Sdd --t->tm_mon; /* Convert from 01-12 to 00-11 */ 53277291Sdd t->tm_mday = ATOI2(arg); 53377291Sdd t->tm_hour = ATOI2(arg); 53477291Sdd t->tm_min = ATOI2(arg); 53577291Sdd break; 53677291Sdd case 4: /* hhmm */ 53777291Sdd t->tm_hour = ATOI2(arg); 53877291Sdd t->tm_min = ATOI2(arg); 53977291Sdd break; 54077291Sdd default: 54177291Sdd goto terr; 54277291Sdd } 54377291Sdd t->tm_isdst = -1; /* Figure out DST. */ 54477291Sdd timet = mktime(t); 54577291Sdd if (timet == -1) 54677291Sddterr: errx(1, 54777291Sdd "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); 54877291Sdd return timet; 54977291Sdd} 55077291Sdd 55177291Sdd 55277291Sdd/* 5531590Srgrimes * onintr -- 5541590Srgrimes * on interrupt, we inform the user how far we've gotten 5551590Srgrimes */ 5561590Srgrimesvoid 5571590Srgrimesonintr(signo) 5581590Srgrimes int signo; 5591590Srgrimes{ 56016438Sache char ct[80]; 56116438Sache struct tm *tm; 5621590Srgrimes 56316438Sache tm = localtime(&buf[0].ut_time); 56474588Sache (void) strftime(ct, sizeof(ct), 56574588Sache d_first ? "%a %e %b %R" : "%a %b %e %R", 56674588Sache tm); 56774588Sache printf("\ninterrupted %s\n", ct); 5681590Srgrimes if (signo == SIGINT) 5691590Srgrimes exit(1); 5701590Srgrimes (void)fflush(stdout); /* fix required for rsh */ 5711590Srgrimes} 572