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;
73227168Sedstatic ARG	*arglist;			/* head of linked list */
741590Srgrimes
75240425Sedstatic SLIST_HEAD(, idtab) idlist;
7611547Sdg
77202197Sedstruct idtab {
7836062Sjb	time_t	logout;				/* log out time */
79202197Sed	char	id[sizeof ((struct utmpx *)0)->ut_id]; /* identifier */
80240425Sed	SLIST_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 */
86230458Shrsstatic const	char *file = NULL;		/* utx.log 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
97227168Sedstatic void	 addarg(int, char *);
98227168Sedstatic time_t	 dateconv(char *);
99227168Sedstatic void	 doentry(struct utmpx *);
100227168Sedstatic void	 hostconv(char *);
101227168Sedstatic void	 printentry(struct utmpx *, struct idtab *);
102227168Sedstatic char	*ttyconv(char *);
103227168Sedstatic int	 want(struct utmpx *);
104227168Sedstatic void	 usage(void);
105227168Sedstatic void	 wtmp(void);
1061590Srgrimes
107227168Sedstatic void
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 --
197230458Shrs *	read through the utx.log file
1981590Srgrimes */
199227168Sedstatic void
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
209240425Sed	SLIST_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);
232230458Shrs	(void) strftime(ct, sizeof(ct), "%+", tm);
233230458Shrs	printf("\n%s begins %s\n", ((file == NULL) ? "utx.log" : file), ct);
2341590Srgrimes}
2351590Srgrimes
2361590Srgrimes/*
23791536Siedowse * doentry --
238230458Shrs *	process a single utx.log entry
23991536Siedowse */
240227168Sedstatic void
241202197Seddoentry(struct utmpx *bp)
24291536Siedowse{
243240425Sed	struct idtab *tt;
24491536Siedowse
245202197Sed	/* the machine stopped */
246202197Sed	if (bp->ut_type == BOOT_TIME || bp->ut_type == SHUTDOWN_TIME) {
24791536Siedowse		/* everybody just logged out */
248240425Sed		while ((tt = SLIST_FIRST(&idlist)) != NULL) {
249240425Sed			SLIST_REMOVE_HEAD(&idlist, list);
250240425Sed			free(tt);
25191536Siedowse		}
252202197Sed		currentout = -bp->ut_tv.tv_sec;
253202197Sed		crmsg = bp->ut_type != SHUTDOWN_TIME ?
25491536Siedowse		    "crash" : "shutdown";
25591536Siedowse		/*
25691536Siedowse		 * if we're in snapshot mode, we want to exit if this
25791536Siedowse		 * shutdown/reboot appears while we we are tracking the
25891536Siedowse		 * active range
25991536Siedowse		 */
26091536Siedowse		if (snaptime && snapfound)
26191536Siedowse			exit(0);
26291536Siedowse		/*
26391536Siedowse		 * don't print shutdown/reboot entries unless flagged for
26491536Siedowse		 */
26591538Siedowse		if (!snaptime && want(bp))
26691536Siedowse			printentry(bp, NULL);
26791536Siedowse		return;
26891536Siedowse	}
269202197Sed	/* date got set */
270202197Sed	if (bp->ut_type == OLD_TIME || bp->ut_type == NEW_TIME) {
27191538Siedowse		if (want(bp) && !snaptime)
27291536Siedowse			printentry(bp, NULL);
27391536Siedowse		return;
27491536Siedowse	}
275202197Sed
276202197Sed	if (bp->ut_type != USER_PROCESS && bp->ut_type != DEAD_PROCESS)
277202197Sed		return;
278202197Sed
279202197Sed	/* find associated identifier */
280240425Sed	SLIST_FOREACH(tt, &idlist, list)
281202197Sed	    if (!memcmp(tt->id, bp->ut_id, sizeof bp->ut_id))
28291536Siedowse		    break;
28391536Siedowse
28491536Siedowse	if (tt == NULL) {
28591536Siedowse		/* add new one */
286202197Sed		tt = malloc(sizeof(struct idtab));
28791536Siedowse		if (tt == NULL)
28896785Sjmallett			errx(1, "malloc failure");
28991536Siedowse		tt->logout = currentout;
290202197Sed		memcpy(tt->id, bp->ut_id, sizeof bp->ut_id);
291240425Sed		SLIST_INSERT_HEAD(&idlist, tt, list);
29291536Siedowse	}
29391536Siedowse
29491536Siedowse	/*
29591536Siedowse	 * print record if not in snapshot mode and wanted
29691536Siedowse	 * or in snapshot mode and in snapshot range
29791536Siedowse	 */
298202197Sed	if (bp->ut_type == USER_PROCESS && (want(bp) ||
299202197Sed	    (bp->ut_tv.tv_sec < snaptime &&
30091536Siedowse	    (tt->logout > snaptime || tt->logout < 1)))) {
30191536Siedowse		snapfound = 1;
30291536Siedowse		printentry(bp, tt);
30391536Siedowse	}
304202197Sed	tt->logout = bp->ut_tv.tv_sec;
30591536Siedowse}
30691536Siedowse
30791536Siedowse/*
30891536Siedowse * printentry --
30991536Siedowse *	output an entry
31091536Siedowse *
31191536Siedowse * If `tt' is non-NULL, use it and `crmsg' to print the logout time or
31291536Siedowse * logout type (crash/shutdown) as appropriate.
31391536Siedowse */
314227168Sedstatic void
315202197Sedprintentry(struct utmpx *bp, struct idtab *tt)
31691536Siedowse{
31791536Siedowse	char ct[80];
31891536Siedowse	struct tm *tm;
31991536Siedowse	time_t	delta;				/* time difference */
32091536Siedowse	time_t	t;
32191536Siedowse
32291538Siedowse	if (maxrec != -1 && !maxrec--)
32391538Siedowse		exit(0);
324202197Sed	t = bp->ut_tv.tv_sec;
32591536Siedowse	tm = localtime(&t);
32691541Siedowse	(void) strftime(ct, sizeof(ct), d_first ?
32791541Siedowse	    (yflag ? "%a %e %b %Y %R" : "%a %e %b %R") :
32891541Siedowse	    (yflag ? "%a %b %e %Y %R" : "%a %b %e %R"), tm);
329202197Sed	switch (bp->ut_type) {
330202197Sed	case BOOT_TIME:
331202197Sed		printf("%-42s", "boot time");
332202197Sed		break;
333202197Sed	case SHUTDOWN_TIME:
334202197Sed		printf("%-42s", "shutdown time");
335202197Sed		break;
336202197Sed	case OLD_TIME:
337202197Sed		printf("%-42s", "old time");
338202197Sed		break;
339202197Sed	case NEW_TIME:
340202197Sed		printf("%-42s", "new time");
341202197Sed		break;
342202197Sed	case USER_PROCESS:
343202197Sed		printf("%-10s %-8s %-22.22s",
344202197Sed		    bp->ut_user, bp->ut_line, bp->ut_host);
345202197Sed		break;
346202197Sed	}
347202197Sed	printf(" %s%c", ct, tt == NULL ? '\n' : ' ');
34891536Siedowse	if (tt == NULL)
34991536Siedowse		return;
35091536Siedowse	if (!tt->logout) {
35191536Siedowse		puts("  still logged in");
35291536Siedowse		return;
35391536Siedowse	}
35491536Siedowse	if (tt->logout < 0) {
35591536Siedowse		tt->logout = -tt->logout;
35691536Siedowse		printf("- %s", crmsg);
35791536Siedowse	} else {
35891536Siedowse		tm = localtime(&tt->logout);
35991536Siedowse		(void) strftime(ct, sizeof(ct), "%R", tm);
36091536Siedowse		printf("- %s", ct);
36191536Siedowse	}
362202197Sed	delta = tt->logout - bp->ut_tv.tv_sec;
36391536Siedowse	if (sflag) {
36491536Siedowse		printf("  (%8ld)\n", (long)delta);
36591536Siedowse	} else {
36691536Siedowse		tm = gmtime(&delta);
36791536Siedowse		(void) strftime(ct, sizeof(ct), width >= 8 ? "%T" : "%R", tm);
36891536Siedowse		if (delta < 86400)
36991536Siedowse			printf("  (%s)\n", ct);
37091536Siedowse		else
37191536Siedowse			printf(" (%ld+%s)\n", (long)delta / 86400, ct);
37291536Siedowse	}
37391536Siedowse}
37491536Siedowse
37591536Siedowse/*
3761590Srgrimes * want --
3771590Srgrimes *	see if want this entry
3781590Srgrimes */
379227168Sedstatic int
380202197Sedwant(struct utmpx *bp)
3811590Srgrimes{
3821590Srgrimes	ARG *step;
3831590Srgrimes
38477291Sdd	if (snaptime)
38577291Sdd		return (NO);
38677291Sdd
3871590Srgrimes	if (!arglist)
3881590Srgrimes		return (YES);
3891590Srgrimes
3901590Srgrimes	for (step = arglist; step; step = step->next)
3911590Srgrimes		switch(step->type) {
3921590Srgrimes		case HOST_TYPE:
393202197Sed			if (!strcasecmp(step->name, bp->ut_host))
3941590Srgrimes				return (YES);
3951590Srgrimes			break;
3961590Srgrimes		case TTY_TYPE:
397202197Sed			if (!strcmp(step->name, bp->ut_line))
3981590Srgrimes				return (YES);
3991590Srgrimes			break;
4001590Srgrimes		case USER_TYPE:
401202197Sed			if (!strcmp(step->name, bp->ut_user))
4021590Srgrimes				return (YES);
4031590Srgrimes			break;
40491536Siedowse		}
4051590Srgrimes	return (NO);
4061590Srgrimes}
4071590Srgrimes
4081590Srgrimes/*
4091590Srgrimes * addarg --
4101590Srgrimes *	add an entry to a linked list of arguments
4111590Srgrimes */
412227168Sedstatic void
413102944Sdwmaloneaddarg(int type, char *arg)
4141590Srgrimes{
4151590Srgrimes	ARG *cur;
4161590Srgrimes
41796785Sjmallett	if ((cur = malloc(sizeof(ARG))) == NULL)
41896785Sjmallett		errx(1, "malloc failure");
4191590Srgrimes	cur->next = arglist;
4201590Srgrimes	cur->type = type;
4211590Srgrimes	cur->name = arg;
4221590Srgrimes	arglist = cur;
4231590Srgrimes}
4241590Srgrimes
4251590Srgrimes/*
4261590Srgrimes * hostconv --
4271590Srgrimes *	convert the hostname to search pattern; if the supplied host name
4281590Srgrimes *	has a domain attached that is the same as the current domain, rip
4291590Srgrimes *	off the domain suffix since that's what login(1) does.
4301590Srgrimes */
431227168Sedstatic void
432102944Sdwmalonehostconv(char *arg)
4331590Srgrimes{
4341590Srgrimes	static int first = 1;
4351590Srgrimes	static char *hostdot, name[MAXHOSTNAMELEN];
4361590Srgrimes	char *argdot;
4371590Srgrimes
4381590Srgrimes	if (!(argdot = strchr(arg, '.')))
4391590Srgrimes		return;
4401590Srgrimes	if (first) {
4411590Srgrimes		first = 0;
4421590Srgrimes		if (gethostname(name, sizeof(name)))
4431590Srgrimes			err(1, "gethostname");
4441590Srgrimes		hostdot = strchr(name, '.');
4451590Srgrimes	}
4461590Srgrimes	if (hostdot && !strcasecmp(hostdot, argdot))
4471590Srgrimes		*argdot = '\0';
4481590Srgrimes}
4491590Srgrimes
4501590Srgrimes/*
4511590Srgrimes * ttyconv --
4521590Srgrimes *	convert tty to correct name.
4531590Srgrimes */
454227168Sedstatic char *
455102944Sdwmalonettyconv(char *arg)
4561590Srgrimes{
4571590Srgrimes	char *mval;
4581590Srgrimes
4591590Srgrimes	/*
4601590Srgrimes	 * kludge -- we assume that all tty's end with
4611590Srgrimes	 * a two character suffix.
4621590Srgrimes	 */
4631590Srgrimes	if (strlen(arg) == 2) {
4641590Srgrimes		/* either 6 for "ttyxx" or 8 for "console" */
46596785Sjmallett		if ((mval = malloc(8)) == NULL)
46696785Sjmallett			errx(1, "malloc failure");
4671590Srgrimes		if (!strcmp(arg, "co"))
4681590Srgrimes			(void)strcpy(mval, "console");
4691590Srgrimes		else {
4701590Srgrimes			(void)strcpy(mval, "tty");
4711590Srgrimes			(void)strcpy(mval + 3, arg);
4721590Srgrimes		}
4731590Srgrimes		return (mval);
4741590Srgrimes	}
4751590Srgrimes	if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1))
4761590Srgrimes		return (arg + 5);
4771590Srgrimes	return (arg);
4781590Srgrimes}
4791590Srgrimes
4801590Srgrimes/*
48177291Sdd * dateconv --
48277291Sdd * 	Convert the snapshot time in command line given in the format
48377291Sdd * 	[[CC]YY]MMDDhhmm[.SS]] to a time_t.
48477291Sdd * 	Derived from atime_arg1() in usr.bin/touch/touch.c
48577291Sdd */
486227168Sedstatic time_t
487102944Sdwmalonedateconv(char *arg)
48877291Sdd{
48977291Sdd        time_t timet;
49077291Sdd        struct tm *t;
49177291Sdd        int yearset;
49277291Sdd        char *p;
49377291Sdd
49477291Sdd        /* Start with the current time. */
49577291Sdd        if (time(&timet) < 0)
49677291Sdd                err(1, "time");
49777291Sdd        if ((t = localtime(&timet)) == NULL)
49877291Sdd                err(1, "localtime");
49977291Sdd
50077291Sdd        /* [[CC]YY]MMDDhhmm[.SS] */
50177291Sdd        if ((p = strchr(arg, '.')) == NULL)
50277291Sdd                t->tm_sec = 0; 		/* Seconds defaults to 0. */
50377291Sdd        else {
50477291Sdd                if (strlen(p + 1) != 2)
50577291Sdd                        goto terr;
50677291Sdd                *p++ = '\0';
50777291Sdd                t->tm_sec = ATOI2(p);
50877291Sdd        }
50977291Sdd
51077291Sdd        yearset = 0;
51177291Sdd        switch (strlen(arg)) {
51277291Sdd        case 12:                	/* CCYYMMDDhhmm */
51377291Sdd                t->tm_year = ATOI2(arg);
51477291Sdd                t->tm_year *= 100;
51577291Sdd                yearset = 1;
516133332Sdwmalone                /* FALLTHROUGH */
51777291Sdd        case 10:                	/* YYMMDDhhmm */
51877291Sdd                if (yearset) {
51977291Sdd                        yearset = ATOI2(arg);
52077291Sdd                        t->tm_year += yearset;
52177291Sdd                } else {
52277291Sdd                        yearset = ATOI2(arg);
52377291Sdd                        if (yearset < 69)
52477291Sdd                                t->tm_year = yearset + 2000;
52577291Sdd                        else
52677291Sdd                                t->tm_year = yearset + 1900;
52777291Sdd                }
52877291Sdd                t->tm_year -= 1900;     /* Convert to UNIX time. */
52977291Sdd                /* FALLTHROUGH */
53077291Sdd        case 8:				/* MMDDhhmm */
53177291Sdd                t->tm_mon = ATOI2(arg);
53277291Sdd                --t->tm_mon;    	/* Convert from 01-12 to 00-11 */
53377291Sdd                t->tm_mday = ATOI2(arg);
53477291Sdd                t->tm_hour = ATOI2(arg);
53577291Sdd                t->tm_min = ATOI2(arg);
53677291Sdd                break;
53777291Sdd        case 4:				/* hhmm */
53877291Sdd                t->tm_hour = ATOI2(arg);
53977291Sdd                t->tm_min = ATOI2(arg);
54077291Sdd                break;
54177291Sdd        default:
54277291Sdd                goto terr;
54377291Sdd        }
54477291Sdd        t->tm_isdst = -1;       	/* Figure out DST. */
54577291Sdd        timet = mktime(t);
54677291Sdd        if (timet == -1)
54777291Sddterr:           errx(1,
54877291Sdd        "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
54977291Sdd        return timet;
55077291Sdd}
551