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 * 4. Neither the name of the University nor the names of its contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
291590Srgrimes
301590Srgrimes#ifndef lint
3178201Sddstatic const char copyright[] =
321590Srgrimes"@(#) Copyright (c) 1987, 1993, 1994\n\
331590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
341590Srgrimes#endif /* not lint */
351590Srgrimes
361590Srgrimes#ifndef lint
3778201Sddstatic const char sccsid[] = "@(#)last.c	8.2 (Berkeley) 4/2/94";
381590Srgrimes#endif /* not lint */
3999112Sobrien#include <sys/cdefs.h>
4099112Sobrien__FBSDID("$FreeBSD$");
411590Srgrimes
421590Srgrimes#include <sys/param.h>
431590Srgrimes#include <sys/stat.h>
441590Srgrimes
451590Srgrimes#include <err.h>
46118077Stjr#include <errno.h>
471590Srgrimes#include <fcntl.h>
4874588Sache#include <langinfo.h>
4916438Sache#include <locale.h>
501590Srgrimes#include <paths.h>
511590Srgrimes#include <signal.h>
521590Srgrimes#include <stdio.h>
531590Srgrimes#include <stdlib.h>
541590Srgrimes#include <string.h>
551590Srgrimes#include <time.h>
56125856Sdwmalone#include <timeconv.h>
571590Srgrimes#include <unistd.h>
58202197Sed#include <utmpx.h>
5911547Sdg#include <sys/queue.h>
601590Srgrimes
611590Srgrimes#define	NO	0				/* false/no */
621590Srgrimes#define	YES	1				/* true/yes */
6377291Sdd#define	ATOI2(ar)	((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
641590Srgrimes
651590Srgrimestypedef struct arg {
661590Srgrimes	char	*name;				/* argument */
671590Srgrimes#define	HOST_TYPE	-2
681590Srgrimes#define	TTY_TYPE	-3
691590Srgrimes#define	USER_TYPE	-4
701590Srgrimes	int	type;				/* type of arg */
711590Srgrimes	struct arg	*next;			/* linked list pointer */
721590Srgrimes} ARG;
731590SrgrimesARG	*arglist;				/* head of linked list */
741590Srgrimes
75202197SedLIST_HEAD(idlisthead, idtab) idlist;
7611547Sdg
77202197Sedstruct idtab {
7836062Sjb	time_t	logout;				/* log out time */
79202197Sed	char	id[sizeof ((struct utmpx *)0)->ut_id]; /* identifier */
80202197Sed	LIST_ENTRY(idtab) list;
8111547Sdg};
821590Srgrimes
8391536Siedowsestatic const	char *crmsg;			/* cause of last reboot */
84202197Sedstatic time_t	currentout;			/* current logout value */
85202197Sedstatic long	maxrec;				/* records to display */
86202197Sedstatic const	char *file = NULL;		/* wtmp file */
8736434Sdannystatic int	sflag = 0;			/* show delta in seconds */
8836434Sdannystatic int	width = 5;			/* show seconds in delta */
8991541Siedowsestatic int	yflag;				/* show year */
9074588Sachestatic int      d_first;
9191538Siedowsestatic int	snapfound = 0;			/* found snapshot entry? */
9277291Sddstatic time_t	snaptime;			/* if != 0, we will only
9377291Sdd						 * report users logged in
9477291Sdd						 * at this snapshot time
9577291Sdd						 */
961590Srgrimes
9792920Simpvoid	 addarg(int, char *);
9892920Simptime_t	 dateconv(char *);
99202197Sedvoid	 doentry(struct utmpx *);
10092920Simpvoid	 hostconv(char *);
101202197Sedvoid	 printentry(struct utmpx *, struct idtab *);
10292920Simpchar	*ttyconv(char *);
103202197Sedint	 want(struct utmpx *);
10492920Simpvoid	 usage(void);
10592920Simpvoid	 wtmp(void);
1061590Srgrimes
10736434Sdannyvoid
10836434Sdannyusage(void)
10936434Sdanny{
11036434Sdanny	(void)fprintf(stderr,
111119023Stjr"usage: last [-swy] [-d [[CC]YY][MMDD]hhmm[.SS]] [-f file] [-h host]\n"
112119023Stjr"            [-n maxrec] [-t tty] [user ...]\n");
11336434Sdanny	exit(1);
11436434Sdanny}
11536434Sdanny
1161590Srgrimesint
117102944Sdwmalonemain(int argc, char *argv[])
1181590Srgrimes{
1191590Srgrimes	int ch;
1201590Srgrimes	char *p;
1211590Srgrimes
12216438Sache	(void) setlocale(LC_TIME, "");
12374588Sache	d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
12416438Sache
1251590Srgrimes	maxrec = -1;
12677291Sdd	snaptime = 0;
127118077Stjr	while ((ch = getopt(argc, argv, "0123456789d:f:h:n:st:wy")) != -1)
1281590Srgrimes		switch (ch) {
1291590Srgrimes		case '0': case '1': case '2': case '3': case '4':
1301590Srgrimes		case '5': case '6': case '7': case '8': case '9':
1311590Srgrimes			/*
1321590Srgrimes			 * kludge: last was originally designed to take
1331590Srgrimes			 * a number after a dash.
1341590Srgrimes			 */
1351590Srgrimes			if (maxrec == -1) {
136106215Smux				p = strchr(argv[optind - 1], ch);
137106215Smux				if (p == NULL)
138106215Smux					p = strchr(argv[optind], ch);
139106215Smux				maxrec = atol(p);
1401590Srgrimes				if (!maxrec)
1411590Srgrimes					exit(0);
1421590Srgrimes			}
1431590Srgrimes			break;
14477291Sdd		case 'd':
14577291Sdd			snaptime = dateconv(optarg);
14677291Sdd			break;
1471590Srgrimes		case 'f':
1481590Srgrimes			file = optarg;
1491590Srgrimes			break;
1501590Srgrimes		case 'h':
1511590Srgrimes			hostconv(optarg);
1521590Srgrimes			addarg(HOST_TYPE, optarg);
1531590Srgrimes			break;
154118077Stjr		case 'n':
155118077Stjr			errno = 0;
156118077Stjr			maxrec = strtol(optarg, &p, 10);
157118077Stjr			if (p == optarg || *p != '\0' || errno != 0 ||
158118077Stjr			    maxrec <= 0)
159118077Stjr				errx(1, "%s: bad line count", optarg);
160118077Stjr			break;
16136434Sdanny		case 's':
16236434Sdanny			sflag++;	/* Show delta as seconds */
16336434Sdanny			break;
1641590Srgrimes		case 't':
1651590Srgrimes			addarg(TTY_TYPE, ttyconv(optarg));
1661590Srgrimes			break;
16736434Sdanny		case 'w':
16836434Sdanny			width = 8;
16936434Sdanny			break;
17091541Siedowse		case 'y':
17191541Siedowse			yflag++;
17291541Siedowse			break;
1731590Srgrimes		case '?':
1741590Srgrimes		default:
17536434Sdanny			usage();
1761590Srgrimes		}
1771590Srgrimes
17836434Sdanny	if (sflag && width == 8) usage();
17936434Sdanny
1801590Srgrimes	if (argc) {
1811590Srgrimes		setlinebuf(stdout);
1821590Srgrimes		for (argv += optind; *argv; ++argv) {
1831590Srgrimes#define	COMPATIBILITY
1841590Srgrimes#ifdef	COMPATIBILITY
1851590Srgrimes			/* code to allow "last p5" to work */
1861590Srgrimes			addarg(TTY_TYPE, ttyconv(*argv));
1871590Srgrimes#endif
1881590Srgrimes			addarg(USER_TYPE, *argv);
1891590Srgrimes		}
1901590Srgrimes	}
1911590Srgrimes	wtmp();
1921590Srgrimes	exit(0);
1931590Srgrimes}
1941590Srgrimes
1951590Srgrimes/*
1961590Srgrimes * wtmp --
1971590Srgrimes *	read through the wtmp file
1981590Srgrimes */
1991590Srgrimesvoid
200102944Sdwmalonewtmp(void)
2011590Srgrimes{
202202643Sed	struct utmpx *buf = NULL;
203202197Sed	struct utmpx *ut;
204202643Sed	static unsigned int amount = 0;
205202197Sed	time_t t;
20616438Sache	char ct[80];
20716438Sache	struct tm *tm;
2081590Srgrimes
209202197Sed	LIST_INIT(&idlist);
210202197Sed	(void)time(&t);
21111547Sdg
212202197Sed	/* Load the last entries from the file. */
213202197Sed	if (setutxdb(UTXDB_LOG, file) != 0)
2141590Srgrimes		err(1, "%s", file);
215202197Sed	while ((ut = getutxent()) != NULL) {
216202643Sed		if (amount % 128 == 0) {
217202643Sed			buf = realloc(buf, (amount + 128) * sizeof *ut);
218202643Sed			if (buf == NULL)
219202643Sed				err(1, "realloc");
220202643Sed		}
221202643Sed		memcpy(&buf[amount++], ut, sizeof *ut);
222202197Sed		if (t > ut->ut_tv.tv_sec)
223202197Sed			t = ut->ut_tv.tv_sec;
224202197Sed	}
225202197Sed	endutxent();
2261590Srgrimes
227202197Sed	/* Display them in reverse order. */
228202197Sed	while (amount > 0)
229202643Sed		doentry(&buf[--amount]);
2301590Srgrimes
23185648Sdillon	tm = localtime(&t);
23278201Sdd	(void) strftime(ct, sizeof(ct), "\nwtmp begins %+\n", tm);
23362871Skris	printf("%s", ct);
2341590Srgrimes}
2351590Srgrimes
2361590Srgrimes/*
23791536Siedowse * doentry --
23891536Siedowse *	process a single wtmp entry
23991536Siedowse */
24091536Siedowsevoid
241202197Seddoentry(struct utmpx *bp)
24291536Siedowse{
243202197Sed	struct idtab	*tt, *ttx;		/* idlist entry */
24491536Siedowse
245202197Sed	/* the machine stopped */
246202197Sed	if (bp->ut_type == BOOT_TIME || bp->ut_type == SHUTDOWN_TIME) {
24791536Siedowse		/* everybody just logged out */
248202197Sed		for (tt = LIST_FIRST(&idlist); tt;) {
24991536Siedowse			LIST_REMOVE(tt, list);
25091536Siedowse			ttx = tt;
25191536Siedowse			tt = LIST_NEXT(tt, list);
25291536Siedowse			free(ttx);
25391536Siedowse		}
254202197Sed		currentout = -bp->ut_tv.tv_sec;
255202197Sed		crmsg = bp->ut_type != SHUTDOWN_TIME ?
25691536Siedowse		    "crash" : "shutdown";
25791536Siedowse		/*
25891536Siedowse		 * if we're in snapshot mode, we want to exit if this
25991536Siedowse		 * shutdown/reboot appears while we we are tracking the
26091536Siedowse		 * active range
26191536Siedowse		 */
26291536Siedowse		if (snaptime && snapfound)
26391536Siedowse			exit(0);
26491536Siedowse		/*
26591536Siedowse		 * don't print shutdown/reboot entries unless flagged for
26691536Siedowse		 */
26791538Siedowse		if (!snaptime && want(bp))
26891536Siedowse			printentry(bp, NULL);
26991536Siedowse		return;
27091536Siedowse	}
271202197Sed	/* date got set */
272202197Sed	if (bp->ut_type == OLD_TIME || bp->ut_type == NEW_TIME) {
27391538Siedowse		if (want(bp) && !snaptime)
27491536Siedowse			printentry(bp, NULL);
27591536Siedowse		return;
27691536Siedowse	}
277202197Sed
278202197Sed	if (bp->ut_type != USER_PROCESS && bp->ut_type != DEAD_PROCESS)
279202197Sed		return;
280202197Sed
281202197Sed	/* find associated identifier */
282202197Sed	LIST_FOREACH(tt, &idlist, list)
283202197Sed	    if (!memcmp(tt->id, bp->ut_id, sizeof bp->ut_id))
28491536Siedowse		    break;
28591536Siedowse
28691536Siedowse	if (tt == NULL) {
28791536Siedowse		/* add new one */
288202197Sed		tt = malloc(sizeof(struct idtab));
28991536Siedowse		if (tt == NULL)
29096785Sjmallett			errx(1, "malloc failure");
29191536Siedowse		tt->logout = currentout;
292202197Sed		memcpy(tt->id, bp->ut_id, sizeof bp->ut_id);
293202197Sed		LIST_INSERT_HEAD(&idlist, tt, list);
29491536Siedowse	}
29591536Siedowse
29691536Siedowse	/*
29791536Siedowse	 * print record if not in snapshot mode and wanted
29891536Siedowse	 * or in snapshot mode and in snapshot range
29991536Siedowse	 */
300202197Sed	if (bp->ut_type == USER_PROCESS && (want(bp) ||
301202197Sed	    (bp->ut_tv.tv_sec < snaptime &&
30291536Siedowse	    (tt->logout > snaptime || tt->logout < 1)))) {
30391536Siedowse		snapfound = 1;
30491536Siedowse		printentry(bp, tt);
30591536Siedowse	}
306202197Sed	tt->logout = bp->ut_tv.tv_sec;
30791536Siedowse}
30891536Siedowse
30991536Siedowse/*
31091536Siedowse * printentry --
31191536Siedowse *	output an entry
31291536Siedowse *
31391536Siedowse * If `tt' is non-NULL, use it and `crmsg' to print the logout time or
31491536Siedowse * logout type (crash/shutdown) as appropriate.
31591536Siedowse */
31691536Siedowsevoid
317202197Sedprintentry(struct utmpx *bp, struct idtab *tt)
31891536Siedowse{
31991536Siedowse	char ct[80];
32091536Siedowse	struct tm *tm;
32191536Siedowse	time_t	delta;				/* time difference */
32291536Siedowse	time_t	t;
32391536Siedowse
32491538Siedowse	if (maxrec != -1 && !maxrec--)
32591538Siedowse		exit(0);
326202197Sed	t = bp->ut_tv.tv_sec;
32791536Siedowse	tm = localtime(&t);
32891541Siedowse	(void) strftime(ct, sizeof(ct), d_first ?
32991541Siedowse	    (yflag ? "%a %e %b %Y %R" : "%a %e %b %R") :
33091541Siedowse	    (yflag ? "%a %b %e %Y %R" : "%a %b %e %R"), tm);
331202197Sed	switch (bp->ut_type) {
332202197Sed	case BOOT_TIME:
333202197Sed		printf("%-42s", "boot time");
334202197Sed		break;
335202197Sed	case SHUTDOWN_TIME:
336202197Sed		printf("%-42s", "shutdown time");
337202197Sed		break;
338202197Sed	case OLD_TIME:
339202197Sed		printf("%-42s", "old time");
340202197Sed		break;
341202197Sed	case NEW_TIME:
342202197Sed		printf("%-42s", "new time");
343202197Sed		break;
344202197Sed	case USER_PROCESS:
345202197Sed		printf("%-10s %-8s %-22.22s",
346202197Sed		    bp->ut_user, bp->ut_line, bp->ut_host);
347202197Sed		break;
348202197Sed	}
349202197Sed	printf(" %s%c", ct, tt == NULL ? '\n' : ' ');
35091536Siedowse	if (tt == NULL)
35191536Siedowse		return;
35291536Siedowse	if (!tt->logout) {
35391536Siedowse		puts("  still logged in");
35491536Siedowse		return;
35591536Siedowse	}
35691536Siedowse	if (tt->logout < 0) {
35791536Siedowse		tt->logout = -tt->logout;
35891536Siedowse		printf("- %s", crmsg);
35991536Siedowse	} else {
36091536Siedowse		tm = localtime(&tt->logout);
36191536Siedowse		(void) strftime(ct, sizeof(ct), "%R", tm);
36291536Siedowse		printf("- %s", ct);
36391536Siedowse	}
364202197Sed	delta = tt->logout - bp->ut_tv.tv_sec;
36591536Siedowse	if (sflag) {
36691536Siedowse		printf("  (%8ld)\n", (long)delta);
36791536Siedowse	} else {
36891536Siedowse		tm = gmtime(&delta);
36991536Siedowse		(void) strftime(ct, sizeof(ct), width >= 8 ? "%T" : "%R", tm);
37091536Siedowse		if (delta < 86400)
37191536Siedowse			printf("  (%s)\n", ct);
37291536Siedowse		else
37391536Siedowse			printf(" (%ld+%s)\n", (long)delta / 86400, ct);
37491536Siedowse	}
37591536Siedowse}
37691536Siedowse
37791536Siedowse/*
3781590Srgrimes * want --
3791590Srgrimes *	see if want this entry
3801590Srgrimes */
3811590Srgrimesint
382202197Sedwant(struct utmpx *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:
395202197Sed			if (!strcasecmp(step->name, bp->ut_host))
3961590Srgrimes				return (YES);
3971590Srgrimes			break;
3981590Srgrimes		case TTY_TYPE:
399202197Sed			if (!strcmp(step->name, bp->ut_line))
4001590Srgrimes				return (YES);
4011590Srgrimes			break;
4021590Srgrimes		case USER_TYPE:
403202197Sed			if (!strcmp(step->name, bp->ut_user))
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
415102944Sdwmaloneaddarg(int type, char *arg)
4161590Srgrimes{
4171590Srgrimes	ARG *cur;
4181590Srgrimes
41996785Sjmallett	if ((cur = malloc(sizeof(ARG))) == NULL)
42096785Sjmallett		errx(1, "malloc failure");
4211590Srgrimes	cur->next = arglist;
4221590Srgrimes	cur->type = type;
4231590Srgrimes	cur->name = arg;
4241590Srgrimes	arglist = cur;
4251590Srgrimes}
4261590Srgrimes
4271590Srgrimes/*
4281590Srgrimes * hostconv --
4291590Srgrimes *	convert the hostname to search pattern; if the supplied host name
4301590Srgrimes *	has a domain attached that is the same as the current domain, rip
4311590Srgrimes *	off the domain suffix since that's what login(1) does.
4321590Srgrimes */
4331590Srgrimesvoid
434102944Sdwmalonehostconv(char *arg)
4351590Srgrimes{
4361590Srgrimes	static int first = 1;
4371590Srgrimes	static char *hostdot, name[MAXHOSTNAMELEN];
4381590Srgrimes	char *argdot;
4391590Srgrimes
4401590Srgrimes	if (!(argdot = strchr(arg, '.')))
4411590Srgrimes		return;
4421590Srgrimes	if (first) {
4431590Srgrimes		first = 0;
4441590Srgrimes		if (gethostname(name, sizeof(name)))
4451590Srgrimes			err(1, "gethostname");
4461590Srgrimes		hostdot = strchr(name, '.');
4471590Srgrimes	}
4481590Srgrimes	if (hostdot && !strcasecmp(hostdot, argdot))
4491590Srgrimes		*argdot = '\0';
4501590Srgrimes}
4511590Srgrimes
4521590Srgrimes/*
4531590Srgrimes * ttyconv --
4541590Srgrimes *	convert tty to correct name.
4551590Srgrimes */
4561590Srgrimeschar *
457102944Sdwmalonettyconv(char *arg)
4581590Srgrimes{
4591590Srgrimes	char *mval;
4601590Srgrimes
4611590Srgrimes	/*
4621590Srgrimes	 * kludge -- we assume that all tty's end with
4631590Srgrimes	 * a two character suffix.
4641590Srgrimes	 */
4651590Srgrimes	if (strlen(arg) == 2) {
4661590Srgrimes		/* either 6 for "ttyxx" or 8 for "console" */
46796785Sjmallett		if ((mval = malloc(8)) == NULL)
46896785Sjmallett			errx(1, "malloc failure");
4691590Srgrimes		if (!strcmp(arg, "co"))
4701590Srgrimes			(void)strcpy(mval, "console");
4711590Srgrimes		else {
4721590Srgrimes			(void)strcpy(mval, "tty");
4731590Srgrimes			(void)strcpy(mval + 3, arg);
4741590Srgrimes		}
4751590Srgrimes		return (mval);
4761590Srgrimes	}
4771590Srgrimes	if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1))
4781590Srgrimes		return (arg + 5);
4791590Srgrimes	return (arg);
4801590Srgrimes}
4811590Srgrimes
4821590Srgrimes/*
48377291Sdd * dateconv --
48477291Sdd * 	Convert the snapshot time in command line given in the format
48577291Sdd * 	[[CC]YY]MMDDhhmm[.SS]] to a time_t.
48677291Sdd * 	Derived from atime_arg1() in usr.bin/touch/touch.c
48777291Sdd */
48877291Sddtime_t
489102944Sdwmalonedateconv(char *arg)
49077291Sdd{
49177291Sdd        time_t timet;
49277291Sdd        struct tm *t;
49377291Sdd        int yearset;
49477291Sdd        char *p;
49577291Sdd
49677291Sdd        /* Start with the current time. */
49777291Sdd        if (time(&timet) < 0)
49877291Sdd                err(1, "time");
49977291Sdd        if ((t = localtime(&timet)) == NULL)
50077291Sdd                err(1, "localtime");
50177291Sdd
50277291Sdd        /* [[CC]YY]MMDDhhmm[.SS] */
50377291Sdd        if ((p = strchr(arg, '.')) == NULL)
50477291Sdd                t->tm_sec = 0; 		/* Seconds defaults to 0. */
50577291Sdd        else {
50677291Sdd                if (strlen(p + 1) != 2)
50777291Sdd                        goto terr;
50877291Sdd                *p++ = '\0';
50977291Sdd                t->tm_sec = ATOI2(p);
51077291Sdd        }
51177291Sdd
51277291Sdd        yearset = 0;
51377291Sdd        switch (strlen(arg)) {
51477291Sdd        case 12:                	/* CCYYMMDDhhmm */
51577291Sdd                t->tm_year = ATOI2(arg);
51677291Sdd                t->tm_year *= 100;
51777291Sdd                yearset = 1;
518133332Sdwmalone                /* FALLTHROUGH */
51977291Sdd        case 10:                	/* YYMMDDhhmm */
52077291Sdd                if (yearset) {
52177291Sdd                        yearset = ATOI2(arg);
52277291Sdd                        t->tm_year += yearset;
52377291Sdd                } else {
52477291Sdd                        yearset = ATOI2(arg);
52577291Sdd                        if (yearset < 69)
52677291Sdd                                t->tm_year = yearset + 2000;
52777291Sdd                        else
52877291Sdd                                t->tm_year = yearset + 1900;
52977291Sdd                }
53077291Sdd                t->tm_year -= 1900;     /* Convert to UNIX time. */
53177291Sdd                /* FALLTHROUGH */
53277291Sdd        case 8:				/* MMDDhhmm */
53377291Sdd                t->tm_mon = ATOI2(arg);
53477291Sdd                --t->tm_mon;    	/* Convert from 01-12 to 00-11 */
53577291Sdd                t->tm_mday = ATOI2(arg);
53677291Sdd                t->tm_hour = ATOI2(arg);
53777291Sdd                t->tm_min = ATOI2(arg);
53877291Sdd                break;
53977291Sdd        case 4:				/* hhmm */
54077291Sdd                t->tm_hour = ATOI2(arg);
54177291Sdd                t->tm_min = ATOI2(arg);
54277291Sdd                break;
54377291Sdd        default:
54477291Sdd                goto terr;
54577291Sdd        }
54677291Sdd        t->tm_isdst = -1;       	/* Figure out DST. */
54777291Sdd        timet = mktime(t);
54877291Sdd        if (timet == -1)
54977291Sddterr:           errx(1,
55077291Sdd        "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
55177291Sdd        return timet;
55277291Sdd}
553