last.c revision 99112
1235537Sgber/*
2235537Sgber * Copyright (c) 1987, 1993, 1994
3235537Sgber *	The Regents of the University of California.  All rights reserved.
4235537Sgber *
5235537Sgber * Redistribution and use in source and binary forms, with or without
6235537Sgber * modification, are permitted provided that the following conditions
7235537Sgber * are met:
8235537Sgber * 1. Redistributions of source code must retain the above copyright
9235537Sgber *    notice, this list of conditions and the following disclaimer.
10235537Sgber * 2. Redistributions in binary form must reproduce the above copyright
11235537Sgber *    notice, this list of conditions and the following disclaimer in the
12235537Sgber *    documentation and/or other materials provided with the distribution.
13235537Sgber * 3. All advertising materials mentioning features or use of this software
14235537Sgber *    must display the following acknowledgement:
15235537Sgber *	This product includes software developed by the University of
16235537Sgber *	California, Berkeley and its contributors.
17235537Sgber * 4. Neither the name of the University nor the names of its contributors
18235537Sgber *    may be used to endorse or promote products derived from this software
19235537Sgber *    without specific prior written permission.
20235537Sgber *
21235537Sgber * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22235537Sgber * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23235537Sgber * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24235537Sgber * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25235537Sgber * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26235537Sgber * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27235537Sgber * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28235537Sgber * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29235537Sgber * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30235537Sgber * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31235537Sgber * SUCH DAMAGE.
32235537Sgber */
33235537Sgber
34235537Sgber#ifndef lint
35235537Sgberstatic const char copyright[] =
36235537Sgber"@(#) Copyright (c) 1987, 1993, 1994\n\
37235537Sgber	The Regents of the University of California.  All rights reserved.\n";
38235537Sgber#endif /* not lint */
39235537Sgber
40235537Sgber#ifndef lint
41235537Sgberstatic const char sccsid[] = "@(#)last.c	8.2 (Berkeley) 4/2/94";
42235537Sgber#endif /* not lint */
43235537Sgber#include <sys/cdefs.h>
44235537Sgber__FBSDID("$FreeBSD: head/usr.bin/last/last.c 99112 2002-06-30 05:25:07Z obrien $");
45235537Sgber
46235537Sgber#include <sys/param.h>
47235537Sgber#include <sys/stat.h>
48235537Sgber
49235537Sgber#include <err.h>
50235537Sgber#include <fcntl.h>
51235537Sgber#include <langinfo.h>
52235537Sgber#include <locale.h>
53235537Sgber#include <paths.h>
54235537Sgber#include <signal.h>
55235537Sgber#include <stdio.h>
56235537Sgber#include <stdlib.h>
57235537Sgber#include <string.h>
58235537Sgber#include <time.h>
59235537Sgber#include <unistd.h>
60235537Sgber#include <utmp.h>
61235537Sgber#include <sys/queue.h>
62235537Sgber
63235537Sgber#define	NO	0				/* false/no */
64235537Sgber#define	YES	1				/* true/yes */
65235537Sgber#define	ATOI2(ar)	((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
66235537Sgber
67235537Sgberstatic struct utmp	buf[1024];		/* utmp read buffer */
68235537Sgber
69235537Sgbertypedef struct arg {
70235537Sgber	char	*name;				/* argument */
71235537Sgber#define	HOST_TYPE	-2
72235537Sgber#define	TTY_TYPE	-3
73235537Sgber#define	USER_TYPE	-4
74235537Sgber	int	type;				/* type of arg */
75235537Sgber	struct arg	*next;			/* linked list pointer */
76235537Sgber} ARG;
77235537SgberARG	*arglist;				/* head of linked list */
78235537Sgber
79235537SgberLIST_HEAD(ttylisthead, ttytab) ttylist;
80235537Sgber
81235537Sgberstruct ttytab {
82235537Sgber	time_t	logout;				/* log out time */
83235537Sgber	char	tty[UT_LINESIZE + 1];		/* terminal name */
84235537Sgber	LIST_ENTRY(ttytab) list;
85235537Sgber};
86235537Sgber
87235537Sgberstatic const	char *crmsg;			/* cause of last reboot */
88235537Sgberstatic long	currentout,			/* current logout value */
89235537Sgber		maxrec;				/* records to display */
90235537Sgberstatic const	char *file = _PATH_WTMP;		/* wtmp file */
91235537Sgberstatic int	sflag = 0;			/* show delta in seconds */
92235537Sgberstatic int	width = 5;			/* show seconds in delta */
93235537Sgberstatic int	yflag;				/* show year */
94235537Sgberstatic int      d_first;
95235537Sgberstatic int	snapfound = 0;			/* found snapshot entry? */
96235537Sgberstatic time_t	snaptime;			/* if != 0, we will only
97235537Sgber						 * report users logged in
98235537Sgber						 * at this snapshot time
99235537Sgber						 */
100235537Sgber
101235537Sgbervoid	 addarg(int, char *);
102235537Sgbertime_t	 dateconv(char *);
103235537Sgbervoid	 doentry(struct utmp *);
104235537Sgbervoid	 hostconv(char *);
105235537Sgbervoid	 onintr(int);
106235537Sgbervoid	 printentry(struct utmp *, struct ttytab *);
107235537Sgberchar	*ttyconv(char *);
108235537Sgberint	 want(struct utmp *);
109235537Sgbervoid	 usage(void);
110235537Sgbervoid	 wtmp(void);
111235537Sgber
112235537Sgbervoid
113235537Sgberusage(void)
114235537Sgber{
115235537Sgber	(void)fprintf(stderr,
116235537Sgber"usage: last [-#] [-y] [-d [[CC]YY][MMDD]hhmm[.SS]] [-f file] [-h host]\n"
117235537Sgber"\t[-t tty] [-s|w] [user ...]\n");
118235537Sgber	exit(1);
119235537Sgber}
120235537Sgber
121235537Sgberint
122235537Sgbermain(argc, argv)
123235537Sgber	int argc;
124235537Sgber	char *argv[];
125235537Sgber{
126235537Sgber	int ch;
127235537Sgber	char *p;
128235537Sgber
129235537Sgber	(void) setlocale(LC_TIME, "");
130235537Sgber	d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
131235537Sgber
132235537Sgber	maxrec = -1;
133235537Sgber	snaptime = 0;
134235537Sgber	while ((ch = getopt(argc, argv, "0123456789d:f:h:st:wy")) != -1)
135235537Sgber		switch (ch) {
136235537Sgber		case '0': case '1': case '2': case '3': case '4':
137235537Sgber		case '5': case '6': case '7': case '8': case '9':
138235537Sgber			/*
139235537Sgber			 * kludge: last was originally designed to take
140235537Sgber			 * a number after a dash.
141235537Sgber			 */
142235537Sgber			if (maxrec == -1) {
143235537Sgber				p = argv[optind - 1];
144235537Sgber				if (p[0] == '-' && p[1] == ch && !p[2])
145235537Sgber					maxrec = atol(++p);
146235537Sgber				else
147235537Sgber					maxrec = atol(argv[optind] + 1);
148235537Sgber				if (!maxrec)
149235537Sgber					exit(0);
150235537Sgber			}
151235537Sgber			break;
152235537Sgber		case 'd':
153235537Sgber			snaptime = dateconv(optarg);
154235537Sgber			break;
155235537Sgber		case 'f':
156235537Sgber			file = optarg;
157235537Sgber			break;
158235537Sgber		case 'h':
159235537Sgber			hostconv(optarg);
160235537Sgber			addarg(HOST_TYPE, optarg);
161235537Sgber			break;
162235537Sgber		case 's':
163235537Sgber			sflag++;	/* Show delta as seconds */
164235537Sgber			break;
165235537Sgber		case 't':
166235537Sgber			addarg(TTY_TYPE, ttyconv(optarg));
167235537Sgber			break;
168235537Sgber		case 'w':
169235537Sgber			width = 8;
170235537Sgber			break;
171235537Sgber		case 'y':
172235537Sgber			yflag++;
173235537Sgber			break;
174235537Sgber		case '?':
175235537Sgber		default:
176235537Sgber			usage();
177262586Sbrueffer		}
178235537Sgber
179235537Sgber	if (sflag && width == 8) usage();
180235537Sgber
181235537Sgber	if (argc) {
182235537Sgber		setlinebuf(stdout);
183235537Sgber		for (argv += optind; *argv; ++argv) {
184235537Sgber#define	COMPATIBILITY
185235537Sgber#ifdef	COMPATIBILITY
186235537Sgber			/* code to allow "last p5" to work */
187235537Sgber			addarg(TTY_TYPE, ttyconv(*argv));
188235537Sgber#endif
189235537Sgber			addarg(USER_TYPE, *argv);
190235537Sgber		}
191235537Sgber	}
192235537Sgber	wtmp();
193235537Sgber	exit(0);
194235537Sgber}
195235537Sgber
196235537Sgber/*
197235537Sgber * wtmp --
198259372Sian *	read through the wtmp file
199259372Sian */
200259372Sianvoid
201259372Sianwtmp()
202259372Sian{
203259372Sian	struct utmp	*bp;			/* current structure */
204259372Sian	struct stat	stb;			/* stat of file for size */
205259372Sian	long	bl;
206259372Sian	int	bytes, wfd;
207259372Sian	char ct[80];
208259372Sian	struct tm *tm;
209259372Sian	time_t	t;
210259372Sian
211259372Sian	LIST_INIT(&ttylist);
212259372Sian
213259372Sian	if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1)
214259372Sian		err(1, "%s", file);
215259372Sian	bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf);
216259372Sian
217259372Sian	(void)time(&t);
218259372Sian	buf[0].ut_time = _time_to_int(t);
219235537Sgber	(void)signal(SIGINT, onintr);
220235537Sgber	(void)signal(SIGQUIT, onintr);
221235537Sgber
222235537Sgber	while (--bl >= 0) {
223235537Sgber		if (lseek(wfd, (off_t)(bl * sizeof(buf)), L_SET) == -1 ||
224235537Sgber		    (bytes = read(wfd, buf, sizeof(buf))) == -1)
225235537Sgber			err(1, "%s", file);
226235537Sgber		for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp)
227235537Sgber			doentry(bp);
228235537Sgber	}
229235537Sgber	t = _int_to_time(buf[0].ut_time);
230235537Sgber	tm = localtime(&t);
231235537Sgber	(void) strftime(ct, sizeof(ct), "\nwtmp begins %+\n", tm);
232235537Sgber	printf("%s", ct);
233235537Sgber}
234235537Sgber
235235537Sgber/*
236235537Sgber * doentry --
237235537Sgber *	process a single wtmp entry
238235537Sgber */
239235537Sgbervoid
240235537Sgberdoentry(bp)
241235537Sgber	struct utmp *bp;
242235537Sgber{
243235537Sgber	struct ttytab	*tt, *ttx;		/* ttylist entry */
244235537Sgber
245235537Sgber	/*
246235537Sgber	 * if the terminal line is '~', the machine stopped.
247235537Sgber	 * see utmp(5) for more info.
248235537Sgber	 */
249235537Sgber	if (bp->ut_line[0] == '~' && !bp->ut_line[1]) {
250235537Sgber		/* everybody just logged out */
251235537Sgber		for (tt = LIST_FIRST(&ttylist); tt;) {
252235537Sgber			LIST_REMOVE(tt, list);
253235537Sgber			ttx = tt;
254235537Sgber			tt = LIST_NEXT(tt, list);
255235537Sgber			free(ttx);
256235537Sgber		}
257235537Sgber		currentout = -bp->ut_time;
258235537Sgber		crmsg = strncmp(bp->ut_name, "shutdown", UT_NAMESIZE) ?
259235537Sgber		    "crash" : "shutdown";
260235537Sgber		/*
261235537Sgber		 * if we're in snapshot mode, we want to exit if this
262235537Sgber		 * shutdown/reboot appears while we we are tracking the
263235537Sgber		 * active range
264235537Sgber		 */
265235537Sgber		if (snaptime && snapfound)
266235537Sgber			exit(0);
267235537Sgber		/*
268235537Sgber		 * don't print shutdown/reboot entries unless flagged for
269235537Sgber		 */
270235537Sgber		if (!snaptime && want(bp))
271235537Sgber			printentry(bp, NULL);
272235537Sgber		return;
273235537Sgber	}
274235537Sgber	/*
275235537Sgber	 * if the line is '{' or '|', date got set; see
276235537Sgber	 * utmp(5) for more info.
277235537Sgber	 */
278235537Sgber	if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|') &&
279235537Sgber	    !bp->ut_line[1]) {
280235537Sgber		if (want(bp) && !snaptime)
281235537Sgber			printentry(bp, NULL);
282235537Sgber		return;
283235537Sgber	}
284235537Sgber	/* find associated tty */
285235537Sgber	LIST_FOREACH(tt, &ttylist, list)
286235537Sgber	    if (!strncmp(tt->tty, bp->ut_line, UT_LINESIZE))
287235537Sgber		    break;
288235537Sgber
289235537Sgber	if (tt == NULL) {
290235537Sgber		/* add new one */
291235537Sgber		tt = malloc(sizeof(struct ttytab));
292235537Sgber		if (tt == NULL)
293235537Sgber			errx(1, "malloc failure");
294235537Sgber		tt->logout = currentout;
295235537Sgber		strncpy(tt->tty, bp->ut_line, UT_LINESIZE);
296235537Sgber		LIST_INSERT_HEAD(&ttylist, tt, list);
297235537Sgber	}
298235537Sgber
299235537Sgber	/*
300235537Sgber	 * print record if not in snapshot mode and wanted
301235537Sgber	 * or in snapshot mode and in snapshot range
302235537Sgber	 */
303235537Sgber	if (bp->ut_name[0] && (want(bp) || (bp->ut_time < snaptime &&
304235537Sgber	    (tt->logout > snaptime || tt->logout < 1)))) {
305235537Sgber		snapfound = 1;
306235537Sgber		/*
307235537Sgber		 * when uucp and ftp log in over a network, the entry in
308235537Sgber		 * the utmp file is the name plus their process id.  See
309235537Sgber		 * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information.
310235537Sgber		 */
311235537Sgber		if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1))
312235537Sgber			bp->ut_line[3] = '\0';
313235537Sgber		else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1))
314235537Sgber			bp->ut_line[4] = '\0';
315235537Sgber		printentry(bp, tt);
316235537Sgber	}
317235537Sgber	tt->logout = bp->ut_time;
318235537Sgber}
319235537Sgber
320235537Sgber/*
321235537Sgber * printentry --
322235537Sgber *	output an entry
323235537Sgber *
324235537Sgber * If `tt' is non-NULL, use it and `crmsg' to print the logout time or
325235537Sgber * logout type (crash/shutdown) as appropriate.
326235537Sgber */
327235537Sgbervoid
328235537Sgberprintentry(bp, tt)
329235537Sgber	struct utmp *bp;
330235537Sgber	struct ttytab *tt;
331235537Sgber{
332235537Sgber	char ct[80];
333235537Sgber	struct tm *tm;
334235537Sgber	time_t	delta;				/* time difference */
335235537Sgber	time_t	t;
336235537Sgber
337235537Sgber	if (maxrec != -1 && !maxrec--)
338235537Sgber		exit(0);
339235537Sgber	t = _int_to_time(bp->ut_time);
340235537Sgber	tm = localtime(&t);
341235537Sgber	(void) strftime(ct, sizeof(ct), d_first ?
342235537Sgber	    (yflag ? "%a %e %b %Y %R" : "%a %e %b %R") :
343235537Sgber	    (yflag ? "%a %b %e %Y %R" : "%a %b %e %R"), tm);
344251022Sgber	printf("%-*.*s %-*.*s %-*.*s %s%c",
345235537Sgber	    UT_NAMESIZE, UT_NAMESIZE, bp->ut_name,
346235537Sgber	    UT_LINESIZE, UT_LINESIZE, bp->ut_line,
347235537Sgber	    UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host,
348235537Sgber	    ct, tt == NULL ? '\n' : ' ');
349235537Sgber	if (tt == NULL)
350235537Sgber		return;
351235537Sgber	if (!tt->logout) {
352235537Sgber		puts("  still logged in");
353235537Sgber		return;
354235537Sgber	}
355235537Sgber	if (tt->logout < 0) {
356235537Sgber		tt->logout = -tt->logout;
357235537Sgber		printf("- %s", crmsg);
358235537Sgber	} else {
359235537Sgber		tm = localtime(&tt->logout);
360235537Sgber		(void) strftime(ct, sizeof(ct), "%R", tm);
361235537Sgber		printf("- %s", ct);
362235537Sgber	}
363235537Sgber	delta = tt->logout - bp->ut_time;
364235537Sgber	if (sflag) {
365235537Sgber		printf("  (%8ld)\n", (long)delta);
366235537Sgber	} else {
367235537Sgber		tm = gmtime(&delta);
368235537Sgber		(void) strftime(ct, sizeof(ct), width >= 8 ? "%T" : "%R", tm);
369235537Sgber		if (delta < 86400)
370235537Sgber			printf("  (%s)\n", ct);
371235537Sgber		else
372235537Sgber			printf(" (%ld+%s)\n", (long)delta / 86400, ct);
373235537Sgber	}
374235537Sgber}
375235537Sgber
376235537Sgber/*
377235537Sgber * want --
378235537Sgber *	see if want this entry
379235537Sgber */
380235537Sgberint
381235537Sgberwant(bp)
382235537Sgber	struct utmp *bp;
383235537Sgber{
384235537Sgber	ARG *step;
385235537Sgber
386235537Sgber	if (snaptime)
387235537Sgber		return (NO);
388235537Sgber
389235537Sgber	if (!arglist)
390235537Sgber		return (YES);
391235537Sgber
392235537Sgber	for (step = arglist; step; step = step->next)
393235537Sgber		switch(step->type) {
394235537Sgber		case HOST_TYPE:
395235537Sgber			if (!strncasecmp(step->name, bp->ut_host, UT_HOSTSIZE))
396235537Sgber				return (YES);
397235537Sgber			break;
398235537Sgber		case TTY_TYPE:
399235537Sgber			if (!strncmp(step->name, bp->ut_line, UT_LINESIZE))
400235537Sgber				return (YES);
401235537Sgber			break;
402235537Sgber		case USER_TYPE:
403235537Sgber			if (!strncmp(step->name, bp->ut_name, UT_NAMESIZE))
404235537Sgber				return (YES);
405235537Sgber			break;
406235537Sgber		}
407235537Sgber	return (NO);
408235537Sgber}
409235537Sgber
410235537Sgber/*
411235537Sgber * addarg --
412235537Sgber *	add an entry to a linked list of arguments
413235537Sgber */
414235537Sgbervoid
415235537Sgberaddarg(type, arg)
416235537Sgber	int type;
417235537Sgber	char *arg;
418235537Sgber{
419235537Sgber	ARG *cur;
420235537Sgber
421235537Sgber	if ((cur = malloc(sizeof(ARG))) == NULL)
422235537Sgber		errx(1, "malloc failure");
423235537Sgber	cur->next = arglist;
424235537Sgber	cur->type = type;
425235537Sgber	cur->name = arg;
426235537Sgber	arglist = cur;
427235537Sgber}
428235537Sgber
429235537Sgber/*
430235537Sgber * hostconv --
431235537Sgber *	convert the hostname to search pattern; if the supplied host name
432235537Sgber *	has a domain attached that is the same as the current domain, rip
433235537Sgber *	off the domain suffix since that's what login(1) does.
434235537Sgber */
435235537Sgbervoid
436235537Sgberhostconv(arg)
437235537Sgber	char *arg;
438235537Sgber{
439235537Sgber	static int first = 1;
440235537Sgber	static char *hostdot, name[MAXHOSTNAMELEN];
441235537Sgber	char *argdot;
442235537Sgber
443235537Sgber	if (!(argdot = strchr(arg, '.')))
444235537Sgber		return;
445235537Sgber	if (first) {
446235537Sgber		first = 0;
447235537Sgber		if (gethostname(name, sizeof(name)))
448235537Sgber			err(1, "gethostname");
449235537Sgber		hostdot = strchr(name, '.');
450235537Sgber	}
451235537Sgber	if (hostdot && !strcasecmp(hostdot, argdot))
452235537Sgber		*argdot = '\0';
453235537Sgber}
454235537Sgber
455235537Sgber/*
456235537Sgber * ttyconv --
457235537Sgber *	convert tty to correct name.
458235537Sgber */
459235537Sgberchar *
460235537Sgberttyconv(arg)
461235537Sgber	char *arg;
462235537Sgber{
463235537Sgber	char *mval;
464235537Sgber
465235537Sgber	/*
466235537Sgber	 * kludge -- we assume that all tty's end with
467235537Sgber	 * a two character suffix.
468235537Sgber	 */
469235537Sgber	if (strlen(arg) == 2) {
470235537Sgber		/* either 6 for "ttyxx" or 8 for "console" */
471235537Sgber		if ((mval = malloc(8)) == NULL)
472235537Sgber			errx(1, "malloc failure");
473235537Sgber		if (!strcmp(arg, "co"))
474235537Sgber			(void)strcpy(mval, "console");
475235537Sgber		else {
476235537Sgber			(void)strcpy(mval, "tty");
477235537Sgber			(void)strcpy(mval + 3, arg);
478235537Sgber		}
479235537Sgber		return (mval);
480235537Sgber	}
481235537Sgber	if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1))
482235537Sgber		return (arg + 5);
483235537Sgber	return (arg);
484235537Sgber}
485235537Sgber
486235537Sgber/*
487235537Sgber * dateconv --
488235537Sgber * 	Convert the snapshot time in command line given in the format
489235537Sgber * 	[[CC]YY]MMDDhhmm[.SS]] to a time_t.
490235537Sgber * 	Derived from atime_arg1() in usr.bin/touch/touch.c
491235537Sgber */
492235537Sgbertime_t
493235537Sgberdateconv(arg)
494235537Sgber        char *arg;
495235537Sgber{
496235537Sgber        time_t timet;
497235537Sgber        struct tm *t;
498235537Sgber        int yearset;
499235537Sgber        char *p;
500235537Sgber
501235537Sgber        /* Start with the current time. */
502235537Sgber        if (time(&timet) < 0)
503235537Sgber                err(1, "time");
504235537Sgber        if ((t = localtime(&timet)) == NULL)
505235537Sgber                err(1, "localtime");
506266065Sian
507235537Sgber        /* [[CC]YY]MMDDhhmm[.SS] */
508235537Sgber        if ((p = strchr(arg, '.')) == NULL)
509235537Sgber                t->tm_sec = 0; 		/* Seconds defaults to 0. */
510235537Sgber        else {
511235537Sgber                if (strlen(p + 1) != 2)
512235537Sgber                        goto terr;
513235537Sgber                *p++ = '\0';
514235537Sgber                t->tm_sec = ATOI2(p);
515235537Sgber        }
516235537Sgber
517235537Sgber        yearset = 0;
518235537Sgber        switch (strlen(arg)) {
519235537Sgber        case 12:                	/* CCYYMMDDhhmm */
520235537Sgber                t->tm_year = ATOI2(arg);
521235537Sgber                t->tm_year *= 100;
522235537Sgber                yearset = 1;
523235537Sgber                /* FALLTHOUGH */
524235537Sgber        case 10:                	/* YYMMDDhhmm */
525235537Sgber                if (yearset) {
526235537Sgber                        yearset = ATOI2(arg);
527235537Sgber                        t->tm_year += yearset;
528235537Sgber                } else {
529235537Sgber                        yearset = ATOI2(arg);
530235537Sgber                        if (yearset < 69)
531235537Sgber                                t->tm_year = yearset + 2000;
532235537Sgber                        else
533235537Sgber                                t->tm_year = yearset + 1900;
534235537Sgber                }
535235537Sgber                t->tm_year -= 1900;     /* Convert to UNIX time. */
536235537Sgber                /* FALLTHROUGH */
537235537Sgber        case 8:				/* MMDDhhmm */
538235537Sgber                t->tm_mon = ATOI2(arg);
539235537Sgber                --t->tm_mon;    	/* Convert from 01-12 to 00-11 */
540235537Sgber                t->tm_mday = ATOI2(arg);
541                t->tm_hour = ATOI2(arg);
542                t->tm_min = ATOI2(arg);
543                break;
544        case 4:				/* hhmm */
545                t->tm_hour = ATOI2(arg);
546                t->tm_min = ATOI2(arg);
547                break;
548        default:
549                goto terr;
550        }
551        t->tm_isdst = -1;       	/* Figure out DST. */
552        timet = mktime(t);
553        if (timet == -1)
554terr:           errx(1,
555        "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
556        return timet;
557}
558
559
560/*
561 * onintr --
562 *	on interrupt, we inform the user how far we've gotten
563 */
564void
565onintr(signo)
566	int signo;
567{
568	char ct[80];
569	struct tm *tm;
570	time_t t = _int_to_time(buf[0].ut_time);
571
572	tm = localtime(&t);
573	(void) strftime(ct, sizeof(ct),
574			d_first ? "%a %e %b %R" : "%a %b %e %R",
575			tm);
576	printf("\ninterrupted %s\n", ct);
577	if (signo == SIGINT)
578		exit(1);
579	(void)fflush(stdout);			/* fix required for rsh */
580}
581