444 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			444 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/* date - Display (or set) the date and time		Author: V. Archer */
 | 
						|
 | 
						|
#include <sys/types.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include <stddef.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <time.h>
 | 
						|
#include <string.h>
 | 
						|
#include <unistd.h>
 | 
						|
 | 
						|
#define	MIN	60L		/* # seconds in a minute */
 | 
						|
#define	HOUR	(60 * MIN)	/* # seconds in an hour */
 | 
						|
#define	DAY	(24 * HOUR)	/* # seconds in a day */
 | 
						|
#define	YEAR	(365 * DAY)	/* # seconds in a (non-leap) year */
 | 
						|
 | 
						|
int qflag, uflag, sflag, Sflag;
 | 
						|
 | 
						|
/* Default output file descriptor.
 | 
						|
 */
 | 
						|
int outfd = 1;
 | 
						|
 | 
						|
_PROTOTYPE(int main, (int argc, char **argv));
 | 
						|
_PROTOTYPE(void putchar, (int c));
 | 
						|
_PROTOTYPE(void pstring, (char *s, int len));
 | 
						|
_PROTOTYPE(void pldecimal, (unsigned long d, int digits));
 | 
						|
_PROTOTYPE(void pdecimal, (int d, int digits));
 | 
						|
_PROTOTYPE(void fmtdate, (char *format, time_t t, struct tm *p));
 | 
						|
_PROTOTYPE(time_t make_time, (char *t));
 | 
						|
_PROTOTYPE(struct tm *september, (time_t *tp));
 | 
						|
_PROTOTYPE(void usage, (void));
 | 
						|
 | 
						|
/* Main module. Handles P1003.2 date and system administrator's date. The
 | 
						|
 * date entered should be given GMT, regardless of the system's TZ!
 | 
						|
 */
 | 
						|
int main(argc, argv)
 | 
						|
int argc;
 | 
						|
char **argv;
 | 
						|
{
 | 
						|
  time_t t;
 | 
						|
  struct tm *tm;
 | 
						|
  char *format;
 | 
						|
  char time_buf[40];
 | 
						|
  int n;
 | 
						|
  int i;
 | 
						|
 | 
						|
  time(&t);
 | 
						|
 | 
						|
  i = 1;
 | 
						|
  while (i < argc && argv[i][0] == '-') {
 | 
						|
	char *opt = argv[i++] + 1, *end;
 | 
						|
 | 
						|
	if (opt[0] == '-' && opt[1] == 0) break;
 | 
						|
 | 
						|
	while (*opt != 0) switch (*opt++) {
 | 
						|
	case 'q':
 | 
						|
		qflag = 1;
 | 
						|
		break;
 | 
						|
	case 's':
 | 
						|
		sflag = 1;
 | 
						|
		break;
 | 
						|
	case 'u':
 | 
						|
		uflag = 1;
 | 
						|
		break;
 | 
						|
	case 'S':
 | 
						|
		Sflag = 1;
 | 
						|
		break;
 | 
						|
	case 't':
 | 
						|
		/* (obsolete, now -r) */
 | 
						|
	case 'r':
 | 
						|
		if (*opt == 0) {
 | 
						|
			if (i == argc) usage();
 | 
						|
			opt = argv[i++];
 | 
						|
		}
 | 
						|
		t = strtoul(opt, &end, 10);
 | 
						|
		if (*end != 0) usage();
 | 
						|
		opt = "";
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		usage();
 | 
						|
	}
 | 
						|
  }
 | 
						|
 | 
						|
  if (!qflag && i < argc && ('0' <= argv[i][0] && argv[i][0] <= '9')) {
 | 
						|
	t = make_time(argv[i++]);
 | 
						|
	sflag = 1;
 | 
						|
  }
 | 
						|
 | 
						|
  format = "%c";
 | 
						|
  if (i < argc && argv[i][0] == '+') format = argv[i++] + 1;
 | 
						|
 | 
						|
  if (i != argc) usage();
 | 
						|
 | 
						|
  if (qflag) {
 | 
						|
	pstring("\nPlease enter date: MMDDYYhhmmss. Then hit the RETURN key.\n", -1);
 | 
						|
	n = read(0, time_buf, sizeof(time_buf));
 | 
						|
	if (n > 0 && time_buf[n-1] == '\n') n--;
 | 
						|
	if (n >= 0) time_buf[n] = 0;
 | 
						|
	t = make_time(time_buf);
 | 
						|
	sflag = 1;
 | 
						|
  }
 | 
						|
 | 
						|
  if (sflag && stime(&t) != 0) {
 | 
						|
	outfd = 2;
 | 
						|
	pstring("No permission to set time\n", -1);
 | 
						|
	return(1);
 | 
						|
  }
 | 
						|
 | 
						|
  tm = Sflag ? september(&t) : uflag ? gmtime(&t) : localtime(&t);
 | 
						|
 | 
						|
  fmtdate(format, t, tm);
 | 
						|
  putchar('\n');
 | 
						|
  return(0);
 | 
						|
}
 | 
						|
 | 
						|
/* Replacement for stdio putchar().
 | 
						|
 */
 | 
						|
void putchar(c)
 | 
						|
int c;
 | 
						|
{
 | 
						|
  static char buf[1024];
 | 
						|
  static char *bp = buf;
 | 
						|
 | 
						|
  if (c != 0) *bp++ = c;
 | 
						|
  if (c == 0 || c == '\n' || bp == buf + sizeof(buf)) {
 | 
						|
	write(outfd, buf, bp - buf);
 | 
						|
	bp = buf;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* Internal function that prints a n-digits number. Replaces stdio in our
 | 
						|
 * specific case.
 | 
						|
 */
 | 
						|
void pldecimal(d, digits)
 | 
						|
unsigned long d;
 | 
						|
int digits;
 | 
						|
{
 | 
						|
  digits--;
 | 
						|
  if (d > 9 || digits > 0) pldecimal(d / 10, digits);
 | 
						|
  putchar('0' + (d % 10));
 | 
						|
}
 | 
						|
 | 
						|
void pdecimal(d, digits)
 | 
						|
int d, digits;
 | 
						|
{
 | 
						|
  pldecimal((unsigned long) d, digits);
 | 
						|
}
 | 
						|
 | 
						|
/* Internal function that prints a fixed-size string. Replaces stdio in our
 | 
						|
 * specific case.
 | 
						|
 */
 | 
						|
void pstring(s, len)
 | 
						|
char *s;
 | 
						|
int len;
 | 
						|
{
 | 
						|
  while (*s)
 | 
						|
	if (len--)
 | 
						|
		putchar(*s++);
 | 
						|
	else
 | 
						|
		break;
 | 
						|
}
 | 
						|
 | 
						|
/* Format the date, using the given locale string. A special case is the
 | 
						|
 * TZ which might be a sign followed by four digits (New format time zone).
 | 
						|
 */
 | 
						|
void fmtdate(format, t, p)
 | 
						|
char *format;
 | 
						|
time_t t;
 | 
						|
struct tm *p;
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  char *s;
 | 
						|
  static char *wday[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
 | 
						|
		       "Thursday", "Friday", "Saturday"};
 | 
						|
  static char *month[] = {"January", "February", "March", "April",
 | 
						|
			"May", "June", "July", "August",
 | 
						|
		    "September", "October", "November", "December"};
 | 
						|
 | 
						|
  while (*format)
 | 
						|
	if (*format == '%') {
 | 
						|
		switch (*++format) {
 | 
						|
		    case 'A':
 | 
						|
			pstring(wday[p->tm_wday], -1);
 | 
						|
			break;
 | 
						|
		    case 'B':
 | 
						|
			pstring(month[p->tm_mon], -1);
 | 
						|
			break;
 | 
						|
		    case 'D':
 | 
						|
			pdecimal(p->tm_mon + 1, 2);
 | 
						|
			putchar('/');
 | 
						|
			pdecimal(p->tm_mday, 2);
 | 
						|
			putchar('/');
 | 
						|
		    case 'y':
 | 
						|
			pdecimal(p->tm_year % 100, 2);
 | 
						|
			break;
 | 
						|
		    case 'H':
 | 
						|
			pdecimal(p->tm_hour, 2);
 | 
						|
			break;
 | 
						|
		    case 'I':
 | 
						|
			i = p->tm_hour % 12;
 | 
						|
			pdecimal(i ? i : 12, 2);
 | 
						|
			break;
 | 
						|
		    case 'M':
 | 
						|
			pdecimal(p->tm_min, 2);
 | 
						|
			break;
 | 
						|
		    case 'X':
 | 
						|
		    case 'T':
 | 
						|
			pdecimal(p->tm_hour, 2);
 | 
						|
			putchar(':');
 | 
						|
			pdecimal(p->tm_min, 2);
 | 
						|
			putchar(':');
 | 
						|
		    case 'S':
 | 
						|
			pdecimal(p->tm_sec, 2);
 | 
						|
			break;
 | 
						|
		    case 'U':
 | 
						|
			pdecimal((p->tm_yday - p->tm_wday + 13) / 7, 2);
 | 
						|
			break;
 | 
						|
		    case 'W':
 | 
						|
			if (--(p->tm_wday) < 0) p->tm_wday = 6;
 | 
						|
			pdecimal((p->tm_yday - p->tm_wday + 13) / 7, 2);
 | 
						|
			if (++(p->tm_wday) > 6) p->tm_wday = 0;
 | 
						|
			break;
 | 
						|
		    case 'Y':
 | 
						|
			pdecimal(p->tm_year + 1900, 4);
 | 
						|
			break;
 | 
						|
		    case 'Z':
 | 
						|
			if (uflag) {
 | 
						|
				s = "GMT";
 | 
						|
			} else {
 | 
						|
				s = (p->tm_isdst == 1) ? tzname[1] : tzname[0];
 | 
						|
			}
 | 
						|
			pstring(s, strlen(s));
 | 
						|
			break;
 | 
						|
		    case 'a':
 | 
						|
			pstring(wday[p->tm_wday], 3);
 | 
						|
			break;
 | 
						|
		    case 'b':
 | 
						|
		    case 'h':
 | 
						|
			pstring(month[p->tm_mon], 3);
 | 
						|
			break;
 | 
						|
		    case 'c':
 | 
						|
			if (!(s = getenv("LC_TIME")))
 | 
						|
				s = "%a %b %e %T %Z %Y";
 | 
						|
			fmtdate(s, t, p);
 | 
						|
			break;
 | 
						|
		    case 'd':
 | 
						|
			pdecimal(p->tm_mday, 2);
 | 
						|
			break;
 | 
						|
		    case 'e':
 | 
						|
			if (p->tm_mday < 10) putchar(' ');
 | 
						|
			pdecimal(p->tm_mday, 1);
 | 
						|
			break;
 | 
						|
		    case 'j':
 | 
						|
			pdecimal(p->tm_yday + 1, 3);
 | 
						|
			break;
 | 
						|
		    case 'm':
 | 
						|
			pdecimal(p->tm_mon + 1, 2);
 | 
						|
			break;
 | 
						|
		    case 'n':	putchar('\n');	break;
 | 
						|
		    case 'p':
 | 
						|
			if (p->tm_hour < 12)
 | 
						|
				putchar('A');
 | 
						|
			else
 | 
						|
				putchar('P');
 | 
						|
			putchar('M');
 | 
						|
			break;
 | 
						|
		    case 'r':
 | 
						|
			fmtdate("%I:%M:%S %p", t, p);
 | 
						|
			break;
 | 
						|
		    case 's':
 | 
						|
			pldecimal((unsigned long) t, 0);
 | 
						|
			break;
 | 
						|
		    case 't':	putchar('\t');	break;
 | 
						|
		    case 'w':
 | 
						|
			putchar('0' + p->tm_wday);
 | 
						|
			break;
 | 
						|
		    case 'x':
 | 
						|
			fmtdate("%B %e %Y", t, p);
 | 
						|
			break;
 | 
						|
		    case '%':	putchar('%');	break;
 | 
						|
		    case '\0':	format--;
 | 
						|
		}
 | 
						|
		format++;
 | 
						|
	} else
 | 
						|
		putchar(*format++);
 | 
						|
}
 | 
						|
 | 
						|
/* Convert a local date string into GMT time in seconds. */
 | 
						|
time_t make_time(t)
 | 
						|
char *t;
 | 
						|
{
 | 
						|
  struct tm tm;				/* user specified time */
 | 
						|
  time_t now;				/* current time */
 | 
						|
  int leap;				/* current year is leap year */
 | 
						|
  int i;				/* general index */
 | 
						|
  int fld;				/* number of fields */
 | 
						|
  int f[6];				/* time fields */
 | 
						|
  static int days_per_month[2][12] = {
 | 
						|
  { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
 | 
						|
  { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }};
 | 
						|
 | 
						|
/* Get current time just in case */
 | 
						|
  now = time((time_t *) 0);
 | 
						|
  tm  = *localtime(&now);
 | 
						|
  tm.tm_sec   = 0;
 | 
						|
  tm.tm_mon++;
 | 
						|
  tm.tm_year %= 100;
 | 
						|
 | 
						|
/* Parse the time */
 | 
						|
#if '0'+1 != '1' || '1'+1 != '2' || '2'+1 != '3' || '3'+1 != '4' || \
 | 
						|
    '4'+1 != '5' || '5'+1 != '6' || '6'+1 != '7' || '7'+1 != '8' || '8'+1 != '9'
 | 
						|
  << Code unsuitable for character collating sequence >>
 | 
						|
#endif
 | 
						|
 | 
						|
  for (fld = 0; fld < sizeof(f)/sizeof(f[0]); fld++) {
 | 
						|
	if (*t == 0) break;
 | 
						|
	f[fld] = 0;
 | 
						|
	for (i = 0; i < 2; i++, t++) {
 | 
						|
		if (*t < '0' || *t > '9') usage();
 | 
						|
		f[fld] = f[fld] * 10 + *t - '0';
 | 
						|
	}
 | 
						|
  }
 | 
						|
 | 
						|
  switch (fld) {
 | 
						|
  case 2:
 | 
						|
	tm.tm_hour = f[0]; tm.tm_min  = f[1]; break;
 | 
						|
 | 
						|
  case 3:
 | 
						|
	tm.tm_hour = f[0]; tm.tm_min  = f[1]; tm.tm_sec  = f[2];
 | 
						|
	break;
 | 
						|
 | 
						|
  case 5:
 | 
						|
  	tm.tm_mon  = f[0]; tm.tm_mday = f[1]; tm.tm_year = f[2];
 | 
						|
	tm.tm_hour = f[3]; tm.tm_min  = f[4];
 | 
						|
	break;
 | 
						|
 | 
						|
  case 6:
 | 
						|
	tm.tm_mon  = f[0]; tm.tm_mday = f[1]; tm.tm_year = f[2];
 | 
						|
	tm.tm_hour = f[3]; tm.tm_min  = f[4]; tm.tm_sec  = f[5];
 | 
						|
	break;
 | 
						|
 | 
						|
  default:
 | 
						|
	usage();
 | 
						|
  }
 | 
						|
 | 
						|
/* Convert the time into seconds since 1 January 1970 */
 | 
						|
  if (tm.tm_year < 70)
 | 
						|
    tm.tm_year += 100;
 | 
						|
  leap = (tm.tm_year % 4 == 0 && tm.tm_year % 400 != 0);
 | 
						|
  if (tm.tm_mon  < 1  || tm.tm_mon  > 12 ||
 | 
						|
      tm.tm_mday < 1  || tm.tm_mday > days_per_month[leap][tm.tm_mon-1] ||
 | 
						|
      tm.tm_hour > 23 || tm.tm_min  > 59) {
 | 
						|
    outfd = 2;
 | 
						|
    pstring("Illegal date format\n", -1);
 | 
						|
    exit(1);
 | 
						|
  }
 | 
						|
 | 
						|
/* Convert the time into Minix time - zone independent code */
 | 
						|
  {
 | 
						|
    time_t utctime;			/* guess at unix time */
 | 
						|
    time_t nextbit;			/* next bit to try */
 | 
						|
    int rv;				/* result of try */
 | 
						|
    struct tm *tmp;			/* local time conversion */
 | 
						|
 | 
						|
#define COMPARE(a,b)	((a) != (b)) ? ((a) - (b)) :
 | 
						|
 | 
						|
    utctime = 1;
 | 
						|
    do {
 | 
						|
      nextbit = utctime;
 | 
						|
      utctime = nextbit << 1;
 | 
						|
    } while (utctime >= 1);
 | 
						|
 | 
						|
    for (utctime = 0; ; nextbit >>= 1) {
 | 
						|
 | 
						|
      utctime |= nextbit;
 | 
						|
      tmp = localtime(&utctime);
 | 
						|
      if (tmp == 0) continue;
 | 
						|
 | 
						|
      rv = COMPARE(tmp->tm_year,    tm.tm_year)
 | 
						|
           COMPARE(tmp->tm_mon + 1, tm.tm_mon)
 | 
						|
	   COMPARE(tmp->tm_mday,    tm.tm_mday)
 | 
						|
	   COMPARE(tmp->tm_hour,    tm.tm_hour)
 | 
						|
	   COMPARE(tmp->tm_min,     tm.tm_min)
 | 
						|
	   COMPARE(tmp->tm_sec,     tm.tm_sec)
 | 
						|
	   0;
 | 
						|
 | 
						|
      if (rv > 0)
 | 
						|
        utctime &= ~nextbit;
 | 
						|
      else if (rv == 0)
 | 
						|
        break;
 | 
						|
 | 
						|
      if (nextbit == 0) {
 | 
						|
	uflag = 1;
 | 
						|
        outfd = 2;
 | 
						|
        pstring("Inexact conversion to UTC from ", -1);
 | 
						|
        fmtdate("%c\n", utctime, localtime(&utctime) );
 | 
						|
	exit(1);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return utctime;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* Correct the time to the reckoning of Eternal September. */
 | 
						|
struct tm *september(tp)
 | 
						|
time_t *tp;
 | 
						|
{
 | 
						|
  time_t t;
 | 
						|
  int days;
 | 
						|
  struct tm *tm;
 | 
						|
 | 
						|
  tm = localtime(tp);
 | 
						|
 | 
						|
  t = *tp - (tm->tm_hour - 12) * 3600L;  /* No zone troubles around noon. */
 | 
						|
  days = 0;
 | 
						|
 | 
						|
  while (tm->tm_year > 93 || (tm->tm_year == 93 && tm->tm_mon >= 8)) {
 | 
						|
	/* Step back a year or a month. */
 | 
						|
	days += tm->tm_year > 93 ? tm->tm_yday+1 : tm->tm_mday;
 | 
						|
	t = *tp - days * (24 * 3600L);
 | 
						|
 | 
						|
	tm = localtime(&t);
 | 
						|
  }
 | 
						|
 | 
						|
  if (days > 0) {
 | 
						|
	tm = localtime(tp);
 | 
						|
	tm->tm_mday = days;
 | 
						|
	tm->tm_year = 93;
 | 
						|
	tm->tm_mon = 8;
 | 
						|
#if SANITY
 | 
						|
	t = mktime(tm);
 | 
						|
	tm = localtime(&t);
 | 
						|
#endif
 | 
						|
  }
 | 
						|
  return tm;
 | 
						|
}
 | 
						|
 | 
						|
/* (Extended) Posix prototype of date. */
 | 
						|
void usage()
 | 
						|
{
 | 
						|
  outfd = 2;
 | 
						|
  pstring("Usage: date [-qsuS] [-r seconds] [[MMDDYY]hhmm[ss]] [+format]\n", -1);
 | 
						|
  exit(1);
 | 
						|
}
 |