last.c revision 77291
1184610Salfred/* 2184610Salfred * Copyright (c) 1987, 1993, 1994 3184610Salfred * The Regents of the University of California. All rights reserved. 4184610Salfred * 5184610Salfred * Redistribution and use in source and binary forms, with or without 6184610Salfred * modification, are permitted provided that the following conditions 7184610Salfred * are met: 8184610Salfred * 1. Redistributions of source code must retain the above copyright 9184610Salfred * notice, this list of conditions and the following disclaimer. 10184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 11184610Salfred * notice, this list of conditions and the following disclaimer in the 12184610Salfred * documentation and/or other materials provided with the distribution. 13184610Salfred * 3. All advertising materials mentioning features or use of this software 14184610Salfred * must display the following acknowledgement: 15184610Salfred * This product includes software developed by the University of 16184610Salfred * California, Berkeley and its contributors. 17184610Salfred * 4. Neither the name of the University nor the names of its contributors 18184610Salfred * may be used to endorse or promote products derived from this software 19184610Salfred * without specific prior written permission. 20184610Salfred * 21184610Salfred * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24184610Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26194677Sthompsa * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27194677Sthompsa * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28194677Sthompsa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29194677Sthompsa * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30194677Sthompsa * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31194677Sthompsa * SUCH DAMAGE. 32194677Sthompsa * 33194677Sthompsa * $FreeBSD: head/usr.bin/last/last.c 77291 2001-05-28 01:22:37Z dd $ 34194677Sthompsa */ 35194677Sthompsa 36194677Sthompsa#ifndef lint 37194677Sthompsastatic char copyright[] = 38194677Sthompsa"@(#) Copyright (c) 1987, 1993, 1994\n\ 39194677Sthompsa The Regents of the University of California. All rights reserved.\n"; 40194677Sthompsa#endif /* not lint */ 41194677Sthompsa 42194677Sthompsa#ifndef lint 43194677Sthompsastatic char sccsid[] = "@(#)last.c 8.2 (Berkeley) 4/2/94"; 44194677Sthompsa#endif /* not lint */ 45194677Sthompsa 46194677Sthompsa#include <sys/param.h> 47194677Sthompsa#include <sys/stat.h> 48194677Sthompsa 49194677Sthompsa#include <err.h> 50188746Sthompsa#include <fcntl.h> 51184610Salfred#include <langinfo.h> 52194228Sthompsa#include <locale.h> 53188942Sthompsa#include <paths.h> 54188942Sthompsa#include <signal.h> 55184610Salfred#include <stdio.h> 56188942Sthompsa#include <stdlib.h> 57184610Salfred#include <string.h> 58184610Salfred#include <time.h> 59184610Salfred#include <unistd.h> 60184610Salfred#include <utmp.h> 61184610Salfred#include <sys/queue.h> 62184610Salfred 63184610Salfred#define NO 0 /* false/no */ 64184610Salfred#define YES 1 /* true/yes */ 65184610Salfred#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; 66184610Salfred 67184610Salfredstatic struct utmp buf[1024]; /* utmp read buffer */ 68184610Salfred 69184610Salfredtypedef struct arg { 70184610Salfred char *name; /* argument */ 71184610Salfred#define HOST_TYPE -2 72184610Salfred#define TTY_TYPE -3 73184610Salfred#define USER_TYPE -4 74184610Salfred int type; /* type of arg */ 75184610Salfred struct arg *next; /* linked list pointer */ 76184610Salfred} ARG; 77184610SalfredARG *arglist; /* head of linked list */ 78184610Salfred 79187259SthompsaLIST_HEAD(ttylisthead, ttytab) ttylist; 80187259Sthompsa 81187259Sthompsastruct ttytab { 82188413Sthompsa time_t logout; /* log out time */ 83187259Sthompsa char tty[UT_LINESIZE + 1]; /* terminal name */ 84187259Sthompsa LIST_ENTRY(ttytab) list; 85184610Salfred}; 86192984Sthompsa 87192984Sthompsastatic long currentout, /* current logout value */ 88184610Salfred maxrec; /* records to display */ 89192984Sthompsastatic char *file = _PATH_WTMP; /* wtmp file */ 90192984Sthompsastatic int sflag = 0; /* show delta in seconds */ 91189265Sthompsastatic int width = 5; /* show seconds in delta */ 92184610Salfredstatic int d_first; 93184610Salfredstatic time_t snaptime; /* if != 0, we will only 94184610Salfred * report users logged in 95184610Salfred * at this snapshot time 96184610Salfred */ 97184610Salfred 98184610Salfredvoid addarg __P((int, char *)); 99184610Salfredvoid hostconv __P((char *)); 100184610Salfredvoid onintr __P((int)); 101184610Salfredchar *ttyconv __P((char *)); 102239299Shselaskytime_t dateconv __P((char *)); 103184610Salfredint want __P((struct utmp *)); 104193045Sthompsavoid wtmp __P((void)); 105193045Sthompsa 106184610Salfredvoid 107239180Shselaskyusage(void) 108192984Sthompsa{ 109192984Sthompsa (void)fprintf(stderr, 110192984Sthompsa "usage: last [-#] [-f file] [-h hostname] [-t tty] [-s|w] [user ...]\n"); 111192984Sthompsa exit(1); 112192984Sthompsa} 113192984Sthompsa 114192984Sthompsaint 115185948Sthompsamain(argc, argv) 116192984Sthompsa int argc; 117185948Sthompsa char *argv[]; 118197570Sthompsa{ 119184610Salfred int ch; 120192984Sthompsa char *p; 121184610Salfred 122184610Salfred (void) setlocale(LC_TIME, ""); 123187259Sthompsa d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 124184610Salfred 125184610Salfred maxrec = -1; 126184610Salfred snaptime = 0; 127190734Sthompsa while ((ch = getopt(argc, argv, "0123456789d:f:h:st:w")) != -1) 128190734Sthompsa switch (ch) { 129190734Sthompsa case '0': case '1': case '2': case '3': case '4': 130184610Salfred case '5': case '6': case '7': case '8': case '9': 131184610Salfred /* 132187259Sthompsa * kludge: last was originally designed to take 133184610Salfred * a number after a dash. 134184610Salfred */ 135184610Salfred if (maxrec == -1) { 136190734Sthompsa p = argv[optind - 1]; 137190734Sthompsa if (p[0] == '-' && p[1] == ch && !p[2]) 138190734Sthompsa maxrec = atol(++p); 139184610Salfred else 140184610Salfred maxrec = atol(argv[optind] + 1); 141184610Salfred if (!maxrec) 142192984Sthompsa exit(0); 143194228Sthompsa } 144194228Sthompsa break; 145194228Sthompsa case 'd': 146194228Sthompsa snaptime = dateconv(optarg); 147194228Sthompsa break; 148194228Sthompsa case 'f': 149194228Sthompsa file = optarg; 150194228Sthompsa break; 151197570Sthompsa case 'h': 152239180Shselasky hostconv(optarg); 153184610Salfred addarg(HOST_TYPE, optarg); 154184610Salfred break; 155184610Salfred case 's': 156184610Salfred sflag++; /* Show delta as seconds */ 157184610Salfred break; 158184610Salfred case 't': 159184610Salfred addarg(TTY_TYPE, ttyconv(optarg)); 160239180Shselasky break; 161184610Salfred case 'w': 162184610Salfred width = 8; 163184610Salfred break; 164184610Salfred case '?': 165184610Salfred default: 166184610Salfred usage(); 167184610Salfred } 168184610Salfred 169184610Salfred if (sflag && width == 8) usage(); 170184610Salfred 171189275Sthompsa if (argc) { 172188942Sthompsa setlinebuf(stdout); 173188942Sthompsa for (argv += optind; *argv; ++argv) { 174212122Sthompsa#define COMPATIBILITY 175184610Salfred#ifdef COMPATIBILITY 176223486Shselasky /* code to allow "last p5" to work */ 177184610Salfred addarg(TTY_TYPE, ttyconv(*argv)); 178184610Salfred#endif 179184610Salfred addarg(USER_TYPE, *argv); 180184610Salfred } 181184610Salfred } 182184610Salfred wtmp(); 183192984Sthompsa exit(0); 184184610Salfred} 185192499Sthompsa 186184610Salfred/* 187184610Salfred * wtmp -- 188184610Salfred * read through the wtmp file 189184610Salfred */ 190184610Salfredvoid 191184610Salfredwtmp() 192184610Salfred{ 193184610Salfred struct utmp *bp; /* current structure */ 194194228Sthompsa struct ttytab *tt, *ttx; /* ttylist entry */ 195184610Salfred struct stat stb; /* stat of file for size */ 196184610Salfred long bl; 197184610Salfred time_t delta; /* time difference */ 198184610Salfred int bytes, wfd; 199184610Salfred char *crmsg; 200192984Sthompsa char ct[80]; 201184610Salfred struct tm *tm; 202184610Salfred int snapfound = 0; /* found snapshot entry? */ 203184610Salfred 204184610Salfred LIST_INIT(&ttylist); 205194228Sthompsa 206189265Sthompsa if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1) 207239180Shselasky err(1, "%s", file); 208184610Salfred bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf); 209184610Salfred 210184610Salfred (void)time(&buf[0].ut_time); 211184610Salfred (void)signal(SIGINT, onintr); 212194228Sthompsa (void)signal(SIGQUIT, onintr); 213184610Salfred 214189265Sthompsa while (--bl >= 0) { 215184610Salfred if (lseek(wfd, (off_t)(bl * sizeof(buf)), L_SET) == -1 || 216184610Salfred (bytes = read(wfd, buf, sizeof(buf))) == -1) 217184610Salfred err(1, "%s", file); 218199816Sthompsa for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) { 219184610Salfred /* 220184610Salfred * if the terminal line is '~', the machine stopped. 221184610Salfred * see utmp(5) for more info. 222189265Sthompsa */ 223194677Sthompsa if (bp->ut_line[0] == '~' && !bp->ut_line[1]) { 224194677Sthompsa /* everybody just logged out */ 225189265Sthompsa for (tt = LIST_FIRST(&ttylist); tt;) { 226184610Salfred LIST_REMOVE(tt, list); 227194228Sthompsa ttx = tt; 228189265Sthompsa tt = LIST_NEXT(tt, list); 229184610Salfred free(ttx); 230194228Sthompsa } 231184610Salfred currentout = -bp->ut_time; 232184610Salfred crmsg = strncmp(bp->ut_name, "shutdown", 233214843Sn_hibma UT_NAMESIZE) ? "crash" : "shutdown"; 234214843Sn_hibma /* 235184610Salfred * if we're in snapshot mode, we want to 236184610Salfred * exit if this shutdown/reboot appears 237184610Salfred * while we we are tracking the active 238184610Salfred * range 239184610Salfred */ 240184610Salfred if (snaptime && snapfound) 241184610Salfred return; 242184610Salfred /* 243184610Salfred * don't print shutdown/reboot entries 244184610Salfred * unless flagged for 245184610Salfred */ 246184610Salfred if (!snaptime && want(bp)) { 247214761Sn_hibma tm = localtime(&bp->ut_time); 248194228Sthompsa (void) strftime(ct, sizeof(ct), 249184610Salfred d_first ? "%a %e %b %R" : 250239299Shselasky "%a %b %e %R", 251239299Shselasky tm); 252239299Shselasky printf("%-*.*s %-*.*s %-*.*s %s\n", 253239299Shselasky UT_NAMESIZE, UT_NAMESIZE, 254184610Salfred bp->ut_name, UT_LINESIZE, 255184610Salfred UT_LINESIZE, bp->ut_line, 256184610Salfred UT_HOSTSIZE, UT_HOSTSIZE, 257239180Shselasky bp->ut_host, ct); 258239180Shselasky if (maxrec != -1 && !--maxrec) 259184610Salfred return; 260239299Shselasky } 261239180Shselasky continue; 262239180Shselasky } 263239299Shselasky /* 264239299Shselasky * if the line is '{' or '|', date got set; see 265239180Shselasky * utmp(5) for more info. 266239180Shselasky */ 267239180Shselasky if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|') 268239180Shselasky && !bp->ut_line[1]) { 269239180Shselasky if (want(bp) && !snaptime) { 270239180Shselasky tm = localtime(&bp->ut_time); 271239299Shselasky (void) strftime(ct, sizeof(ct), 272239180Shselasky d_first ? "%a %e %b %R" : 273239180Shselasky "%a %b %e %R", 274239180Shselasky tm); 275194677Sthompsa printf("%-*.*s %-*.*s %-*.*s %s\n", 276184610Salfred UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, 277194677Sthompsa UT_LINESIZE, UT_LINESIZE, bp->ut_line, 278194677Sthompsa UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, 279184610Salfred ct); 280184610Salfred if (maxrec && !--maxrec) 281184610Salfred return; 282184610Salfred } 283184610Salfred continue; 284188413Sthompsa } 285194677Sthompsa /* find associated tty */ 286194677Sthompsa LIST_FOREACH(tt, &ttylist, list) 287184610Salfred if (!strncmp(tt->tty, bp->ut_line, UT_LINESIZE)) 288194677Sthompsa break; 289194228Sthompsa 290184610Salfred if (tt == NULL) { 291184610Salfred /* add new one */ 292184610Salfred tt = malloc(sizeof(struct ttytab)); 293184610Salfred if (tt == NULL) 294194677Sthompsa err(1, "malloc failure"); 295188413Sthompsa tt->logout = currentout; 296194677Sthompsa strncpy(tt->tty, bp->ut_line, UT_LINESIZE); 297188413Sthompsa LIST_INSERT_HEAD(&ttylist, tt, list); 298184610Salfred } 299184610Salfred 300184610Salfred /* 301184610Salfred * print record if not in snapshot mode and wanted 302184610Salfred * or in snapshot mode and in snapshot range 303184610Salfred */ 304184610Salfred if (bp->ut_name[0] && (want(bp) || 305194677Sthompsa (bp->ut_time < snaptime && 306184610Salfred (tt->logout > snaptime || tt->logout < 1)))) { 307194677Sthompsa snapfound = 1; 308194677Sthompsa /* 309194677Sthompsa * when uucp and ftp log in over a network, the entry in 310184610Salfred * the utmp file is the name plus their process id. See 311194677Sthompsa * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information. 312194677Sthompsa */ 313184610Salfred if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1)) 314184610Salfred bp->ut_line[3] = '\0'; 315194677Sthompsa else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1)) 316194677Sthompsa bp->ut_line[4] = '\0'; 317184610Salfred tm = localtime(&bp->ut_time); 318184610Salfred (void) strftime(ct, sizeof(ct), 319188413Sthompsa d_first ? "%a %e %b %R" : 320194677Sthompsa "%a %b %e %R", 321194228Sthompsa tm); 322184610Salfred printf("%-*.*s %-*.*s %-*.*s %s ", 323184610Salfred UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, 324184610Salfred UT_LINESIZE, UT_LINESIZE, bp->ut_line, 325194677Sthompsa UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, 326188413Sthompsa ct); 327194677Sthompsa if (!tt->logout) 328188413Sthompsa puts(" still logged in"); 329184610Salfred else { 330184610Salfred if (tt->logout < 0) { 331184610Salfred tt->logout = -tt->logout; 332184610Salfred printf("- %s", crmsg); 333184610Salfred } 334184610Salfred else { 335192984Sthompsa tm = localtime(&tt->logout); 336184610Salfred (void) strftime(ct, sizeof(ct), "%R", tm); 337184610Salfred printf("- %s", ct); 338184610Salfred } 339194228Sthompsa delta = tt->logout - bp->ut_time; 340184610Salfred if ( sflag ) { 341184610Salfred printf(" (%8lu)\n", 342184610Salfred delta); 343192984Sthompsa } else { 344184610Salfred tm = gmtime(&delta); 345184610Salfred (void) strftime(ct, sizeof(ct), 346184610Salfred width >= 8 ? "%T" : "%R", 347194228Sthompsa tm); 348184610Salfred if (delta < 86400) 349184610Salfred printf(" (%s)\n", ct); 350184610Salfred else 351192984Sthompsa printf(" (%ld+%s)\n", 352184610Salfred delta / 86400, ct); 353184610Salfred } 354184610Salfred } 355194228Sthompsa LIST_REMOVE(tt, list); 356184610Salfred free(tt); 357184610Salfred if (maxrec != -1 && !--maxrec) 358184610Salfred return; 359192984Sthompsa } else { 360184610Salfred tt->logout = bp->ut_time; 361184610Salfred } 362184610Salfred } 363194228Sthompsa } 364184610Salfred tm = localtime(&buf[0].ut_time); 365184610Salfred (void) strftime(ct, sizeof(ct), "\nwtmp begins %c\n", tm); 366184610Salfred printf("%s", ct); 367192984Sthompsa} 368184610Salfred 369184610Salfred/* 370184610Salfred * want -- 371184610Salfred * see if want this entry 372184610Salfred */ 373184610Salfredint 374184610Salfredwant(bp) 375192984Sthompsa struct utmp *bp; 376184610Salfred{ 377184610Salfred ARG *step; 378184610Salfred 379184610Salfred if (snaptime) 380184610Salfred return (NO); 381184610Salfred 382184610Salfred if (!arglist) 383184610Salfred return (YES); 384184610Salfred 385184610Salfred for (step = arglist; step; step = step->next) 386184610Salfred switch(step->type) { 387184610Salfred case HOST_TYPE: 388184610Salfred if (!strncasecmp(step->name, bp->ut_host, UT_HOSTSIZE)) 389184610Salfred return (YES); 390184610Salfred break; 391184610Salfred case TTY_TYPE: 392184610Salfred if (!strncmp(step->name, bp->ut_line, UT_LINESIZE)) 393184610Salfred return (YES); 394184610Salfred break; 395184610Salfred case USER_TYPE: 396184610Salfred if (!strncmp(step->name, bp->ut_name, UT_NAMESIZE)) 397184610Salfred return (YES); 398184610Salfred break; 399184610Salfred } 400184610Salfred return (NO); 401184610Salfred} 402184610Salfred 403184610Salfred/* 404184610Salfred * addarg -- 405184610Salfred * add an entry to a linked list of arguments 406184610Salfred */ 407184610Salfredvoid 408184610Salfredaddarg(type, arg) 409184610Salfred int type; 410184610Salfred char *arg; 411184610Salfred{ 412184610Salfred ARG *cur; 413184610Salfred 414184610Salfred if (!(cur = (ARG *)malloc((u_int)sizeof(ARG)))) 415184610Salfred err(1, "malloc failure"); 416184610Salfred cur->next = arglist; 417184610Salfred cur->type = type; 418184610Salfred cur->name = arg; 419184610Salfred arglist = cur; 420184610Salfred} 421184610Salfred 422184610Salfred/* 423184610Salfred * hostconv -- 424184610Salfred * convert the hostname to search pattern; if the supplied host name 425192984Sthompsa * has a domain attached that is the same as the current domain, rip 426184610Salfred * off the domain suffix since that's what login(1) does. 427184610Salfred */ 428184610Salfredvoid 429184610Salfredhostconv(arg) 430184610Salfred char *arg; 431184610Salfred{ 432184610Salfred static int first = 1; 433184610Salfred static char *hostdot, name[MAXHOSTNAMELEN]; 434192984Sthompsa char *argdot; 435184610Salfred 436184610Salfred if (!(argdot = strchr(arg, '.'))) 437184610Salfred return; 438184610Salfred if (first) { 439184610Salfred first = 0; 440184610Salfred if (gethostname(name, sizeof(name))) 441184610Salfred err(1, "gethostname"); 442184610Salfred hostdot = strchr(name, '.'); 443184610Salfred } 444184610Salfred if (hostdot && !strcasecmp(hostdot, argdot)) 445184610Salfred *argdot = '\0'; 446192984Sthompsa} 447193045Sthompsa 448184610Salfred/* 449184610Salfred * ttyconv -- 450184610Salfred * convert tty to correct name. 451184610Salfred */ 452184610Salfredchar * 453184610Salfredttyconv(arg) 454184610Salfred char *arg; 455194228Sthompsa{ 456188413Sthompsa char *mval; 457184610Salfred 458184610Salfred /* 459194228Sthompsa * kludge -- we assume that all tty's end with 460184610Salfred * a two character suffix. 461184610Salfred */ 462197570Sthompsa if (strlen(arg) == 2) { 463197570Sthompsa /* either 6 for "ttyxx" or 8 for "console" */ 464197570Sthompsa if (!(mval = malloc((u_int)8))) 465197570Sthompsa err(1, "malloc failure"); 466197570Sthompsa if (!strcmp(arg, "co")) 467197570Sthompsa (void)strcpy(mval, "console"); 468197570Sthompsa else { 469 (void)strcpy(mval, "tty"); 470 (void)strcpy(mval + 3, arg); 471 } 472 return (mval); 473 } 474 if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1)) 475 return (arg + 5); 476 return (arg); 477} 478 479/* 480 * dateconv -- 481 * Convert the snapshot time in command line given in the format 482 * [[CC]YY]MMDDhhmm[.SS]] to a time_t. 483 * Derived from atime_arg1() in usr.bin/touch/touch.c 484 */ 485time_t 486dateconv(arg) 487 char *arg; 488{ 489 time_t timet; 490 struct tm *t; 491 int yearset; 492 char *p; 493 494 /* Start with the current time. */ 495 if (time(&timet) < 0) 496 err(1, "time"); 497 if ((t = localtime(&timet)) == NULL) 498 err(1, "localtime"); 499 500 /* [[CC]YY]MMDDhhmm[.SS] */ 501 if ((p = strchr(arg, '.')) == NULL) 502 t->tm_sec = 0; /* Seconds defaults to 0. */ 503 else { 504 if (strlen(p + 1) != 2) 505 goto terr; 506 *p++ = '\0'; 507 t->tm_sec = ATOI2(p); 508 } 509 510 yearset = 0; 511 switch (strlen(arg)) { 512 case 12: /* CCYYMMDDhhmm */ 513 t->tm_year = ATOI2(arg); 514 t->tm_year *= 100; 515 yearset = 1; 516 /* FALLTHOUGH */ 517 case 10: /* YYMMDDhhmm */ 518 if (yearset) { 519 yearset = ATOI2(arg); 520 t->tm_year += yearset; 521 } else { 522 yearset = ATOI2(arg); 523 if (yearset < 69) 524 t->tm_year = yearset + 2000; 525 else 526 t->tm_year = yearset + 1900; 527 } 528 t->tm_year -= 1900; /* Convert to UNIX time. */ 529 /* FALLTHROUGH */ 530 case 8: /* MMDDhhmm */ 531 t->tm_mon = ATOI2(arg); 532 --t->tm_mon; /* Convert from 01-12 to 00-11 */ 533 t->tm_mday = ATOI2(arg); 534 t->tm_hour = ATOI2(arg); 535 t->tm_min = ATOI2(arg); 536 break; 537 case 4: /* hhmm */ 538 t->tm_hour = ATOI2(arg); 539 t->tm_min = ATOI2(arg); 540 break; 541 default: 542 goto terr; 543 } 544 t->tm_isdst = -1; /* Figure out DST. */ 545 timet = mktime(t); 546 if (timet == -1) 547terr: errx(1, 548 "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); 549 return timet; 550} 551 552 553/* 554 * onintr -- 555 * on interrupt, we inform the user how far we've gotten 556 */ 557void 558onintr(signo) 559 int signo; 560{ 561 char ct[80]; 562 struct tm *tm; 563 564 tm = localtime(&buf[0].ut_time); 565 (void) strftime(ct, sizeof(ct), 566 d_first ? "%a %e %b %R" : "%a %b %e %R", 567 tm); 568 printf("\ninterrupted %s\n", ct); 569 if (signo == SIGINT) 570 exit(1); 571 (void)fflush(stdout); /* fix required for rsh */ 572} 573