Exemple #1
0
static int
bin_strftime(char *nam, char **argv, Options ops, UNUSED(int func))
{
    int bufsize, x;
    char *endptr = NULL, *scalar = NULL, *buffer;
    time_t secs;
    struct tm *t;

    if (OPT_ISSET(ops,'s')) {
	scalar = OPT_ARG(ops, 's');
	if (!isident(scalar)) {
	    zwarnnam(nam, "not an identifier: %s", scalar);
	    return 1;
	}
    }
    if (OPT_ISSET(ops, 'r'))
	return reverse_strftime(nam, argv, scalar, OPT_ISSET(ops, 'q'));

    errno = 0;
    secs = (time_t)strtoul(argv[1], &endptr, 10);
    if (errno != 0) {
	zwarnnam(nam, "%s: %e", argv[1], errno);
	return 1;
    } else if (*endptr != '\0') {
	zwarnnam(nam, "%s: invalid decimal number", argv[1]);
	return 1;
    }

    t = localtime(&secs);
    if (!t) {
	zwarnnam(nam, "%s: unable to convert to time", argv[1]);
	return 1;
    }
    bufsize = strlen(argv[0]) * 8;
    buffer = zalloc(bufsize);

    for (x=0; x < 4; x++) {
        if (ztrftime(buffer, bufsize, argv[0], t) >= 0)
	    break;
	buffer = zrealloc(buffer, bufsize *= 2);
    }

    if (scalar) {
	setsparam(scalar, metafy(buffer, -1, META_DUP));
    } else {
	printf("%s\n", buffer);
    }
    zfree(buffer, bufsize);

    return 0;
}
Exemple #2
0
static void
stattimeprint(time_t tim, char *outbuf, int flags)
{
    if (flags & STF_RAW) {
	sprintf(outbuf, "%ld", (unsigned long)tim);
	if (flags & STF_STRING)
	    strcat(outbuf, " (");
    }
    if (flags & STF_STRING) {
	char *oend = outbuf + strlen(outbuf);
	ztrftime(oend, 40, timefmt, (flags & STF_GMT) ? gmtime(&tim) :
		 localtime(&tim));
	if (flags & STF_RAW)
	    strcat(outbuf, ")");
    }
}
Exemple #3
0
static void
stattimeprint(time_t tim, long nsecs, char *outbuf, int flags)
{
    if (flags & STF_RAW) {
	sprintf(outbuf, "%ld", (unsigned long)tim);
	if (flags & STF_STRING)
	    strcat(outbuf, " (");
    }
    if (flags & STF_STRING) {
	char *oend = outbuf + strlen(outbuf);
	/* Where the heck does "40" come from? */
	ztrftime(oend, 40, timefmt, (flags & STF_GMT) ? gmtime(&tim) :
			   localtime(&tim), nsecs);
	if (flags & STF_RAW)
	    strcat(oend, ")");
    }
}
Exemple #4
0
static int
putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
{
    char *ss, *hostnam;
    int t0, arg, test, sep, j, numjobs;
    struct tm *tm;
    struct timezone dummy_tz;
    struct timeval tv;
    time_t timet;
    Nameddir nd;

    for (; *bv->fm && *bv->fm != endchar; bv->fm++) {
	arg = 0;
	if (*bv->fm == '%' && isset(PROMPTPERCENT)) {
	    int minus = 0;
	    bv->fm++;
	    if (*bv->fm == '-') {
		minus = 1;
		bv->fm++;
	    }
	    if (idigit(*bv->fm)) {
		arg = zstrtol(bv->fm, &bv->fm, 10);
		if (minus)
		    arg *= -1;
	    } else if (minus)
		arg = -1;
	    if (*bv->fm == '(') {
		int tc, otruncwidth;

		if (idigit(*++bv->fm)) {
		    arg = zstrtol(bv->fm, &bv->fm, 10);
		} else if (arg < 0) {
		    /* negative numbers don't make sense here */
		    arg *= -1;
		}
		test = 0;
		ss = pwd;
		switch (tc = *bv->fm) {
		case 'c':
		case '.':
		case '~':
		    if ((nd = finddir(ss))) {
			arg--;
			ss += strlen(nd->dir);
		    } /*FALLTHROUGH*/
		case '/':
		case 'C':
		    /* `/' gives 0, `/any' gives 1, etc. */
		    if (*ss++ == '/' && *ss)
			arg--;
		    for (; *ss; ss++)
			if (*ss == '/')
			    arg--;
		    if (arg <= 0)
			test = 1;
		    break;
		case 't':
		case 'T':
		case 'd':
		case 'D':
		case 'w':
		    timet = time(NULL);
		    tm = localtime(&timet);
		    switch (tc) {
		    case 't':
			test = (arg == tm->tm_min);
			break;
		    case 'T':
			test = (arg == tm->tm_hour);
			break;
		    case 'd':
			test = (arg == tm->tm_mday);
			break;
		    case 'D':
			test = (arg == tm->tm_mon);
			break;
		    case 'w':
			test = (arg == tm->tm_wday);
			break;
		    }
		    break;
		case '?':
		    if (lastval == arg)
			test = 1;
		    break;
		case '#':
		    if (geteuid() == (uid_t)arg)
			test = 1;
		    break;
		case 'g':
		    if (getegid() == (gid_t)arg)
			test = 1;
		    break;
		case 'j':
		    for (numjobs = 0, j = 1; j <= maxjob; j++)
			if (jobtab[j].stat && jobtab[j].procs &&
		    	    !(jobtab[j].stat & STAT_NOPRINT)) numjobs++;
		    if (numjobs >= arg)
		    	test = 1;
		    break;
		case 'l':
		    *bv->bp = '\0';
		    countprompt(bv->bufline, &t0, 0, 0);
		    if (minus)
			t0 = zterm_columns - t0;
		    if (t0 >= arg)
			test = 1;
		    break;
		case 'e':
		    {
			Funcstack fsptr = funcstack;
			test = arg;
			while (fsptr && test > 0) {
			    test--;
			    fsptr = fsptr->prev;
			}
			test = !test;
		    }
		    break;
		case 'L':
		    if (shlvl >= arg)
			test = 1;
		    break;
		case 'S':
		    if (time(NULL) - shtimer.tv_sec >= arg)
			test = 1;
		    break;
		case 'v':
		    if (arrlen(psvar) >= arg)
			test = 1;
		    break;
		case 'V':
		    if (arrlen(psvar) >= arg) {
			if (*psvar[(arg ? arg : 1) - 1])
			    test = 1;
		    }
		    break;
		case '_':
		    test = (cmdsp >= arg);
		    break;
		case '!':
		    test = privasserted();
		    break;
		default:
		    test = -1;
		    break;
		}
		if (!*bv->fm || !(sep = *++bv->fm))
		    return 0;
		bv->fm++;
		/* Don't do the current truncation until we get back */
		otruncwidth = bv->truncwidth;
		bv->truncwidth = 0;
		if (!putpromptchar(test == 1 && doprint, sep,
				   txtchangep) || !*++bv->fm ||
		    !putpromptchar(test == 0 && doprint, ')',
				   txtchangep)) {
		    bv->truncwidth = otruncwidth;
		    return 0;
		}
		bv->truncwidth = otruncwidth;
		continue;
	    }
	    if (!doprint)
		switch(*bv->fm) {
		  case '[':
		    while(idigit(*++bv->fm));
		    while(*++bv->fm != ']');
		    continue;
		  case '<':
		    while(*++bv->fm != '<');
		    continue;
		  case '>':
		    while(*++bv->fm != '>');
		    continue;
		  case 'D':
		    if(bv->fm[1]=='{')
			while(*++bv->fm != '}');
		    continue;
		  default:
		    continue;
		}
	    switch (*bv->fm) {
	    case '~':
		promptpath(pwd, arg, 1);
		break;
	    case 'd':
	    case '/':
		promptpath(pwd, arg, 0);
		break;
	    case 'c':
	    case '.':
		promptpath(pwd, arg ? arg : 1, 1);
		break;
	    case 'C':
		promptpath(pwd, arg ? arg : 1, 0);
		break;
	    case 'N':
		promptpath(scriptname ? scriptname : argzero, arg, 0);
		break;
	    case 'h':
	    case '!':
		addbufspc(DIGBUFSIZE);
		convbase(bv->bp, curhist, 10);
		bv->bp += strlen(bv->bp);
		break;
	    case 'j':
		for (numjobs = 0, j = 1; j <= maxjob; j++)
		    if (jobtab[j].stat && jobtab[j].procs &&
		    	!(jobtab[j].stat & STAT_NOPRINT)) numjobs++;
		addbufspc(DIGBUFSIZE);
		sprintf(bv->bp, "%d", numjobs);
		bv->bp += strlen(bv->bp);
		break;
	    case 'M':
		queue_signals();
		if ((hostnam = getsparam("HOST")))
		    stradd(hostnam);
		unqueue_signals();
		break;
	    case 'm':
		if (!arg)
		    arg++;
		queue_signals();
		if (!(hostnam = getsparam("HOST")))
		    break;
		if (arg < 0) {
		    for (ss = hostnam + strlen(hostnam); ss > hostnam; ss--)
			if (ss[-1] == '.' && !++arg)
			    break;
		    stradd(ss);
		} else {
		    for (ss = hostnam; *ss; ss++)
			if (*ss == '.' && !--arg)
			    break;
		    stradd(*ss ? dupstrpfx(hostnam, ss - hostnam) : hostnam);
		}
		unqueue_signals();
		break;
	    case 'S':
		txtchangeset(txtchangep, TXTSTANDOUT, TXTNOSTANDOUT);
		txtset(TXTSTANDOUT);
		tsetcap(TCSTANDOUTBEG, TSC_PROMPT);
		break;
	    case 's':
		txtchangeset(txtchangep, TXTNOSTANDOUT, TXTSTANDOUT);
		txtunset(TXTSTANDOUT);
		tsetcap(TCSTANDOUTEND, TSC_PROMPT|TSC_DIRTY);
		break;
	    case 'B':
		txtchangeset(txtchangep, TXTBOLDFACE, TXTNOBOLDFACE);
		txtset(TXTBOLDFACE);
		tsetcap(TCBOLDFACEBEG, TSC_PROMPT|TSC_DIRTY);
		break;
	    case 'b':
		txtchangeset(txtchangep, TXTNOBOLDFACE, TXTBOLDFACE);
		txtchangeset(txtchangep, TXTNOSTANDOUT, TXTSTANDOUT);
		txtchangeset(txtchangep, TXTNOUNDERLINE, TXTUNDERLINE);
		txtunset(TXTBOLDFACE);
		tsetcap(TCALLATTRSOFF, TSC_PROMPT|TSC_DIRTY);
		break;
	    case 'U':
		txtchangeset(txtchangep, TXTUNDERLINE, TXTNOUNDERLINE);
		txtset(TXTUNDERLINE);
		tsetcap(TCUNDERLINEBEG, TSC_PROMPT);
		break;
	    case 'u':
		txtchangeset(txtchangep, TXTNOUNDERLINE, TXTUNDERLINE);
		txtunset(TXTUNDERLINE);
		tsetcap(TCUNDERLINEEND, TSC_PROMPT|TSC_DIRTY);
		break;
	    case 'F':
		arg = parsecolorchar(arg, 1);
		if (arg >= 0 && !(arg & TXTNOFGCOLOUR)) {
		    txtchangeset(txtchangep, arg & TXT_ATTR_FG_ON_MASK,
				 TXTNOFGCOLOUR);
		    txtset(arg & TXT_ATTR_FG_ON_MASK);
		    set_colour_attribute(arg, COL_SEQ_FG, TSC_PROMPT);
		    break;
		}
		/* else FALLTHROUGH */
	    case 'f':
		txtchangeset(txtchangep, TXTNOFGCOLOUR, TXT_ATTR_FG_ON_MASK);
		txtunset(TXT_ATTR_FG_ON_MASK);
		set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_PROMPT);
		break;
	    case 'K':
		arg = parsecolorchar(arg, 0);
		if (arg >= 0 && !(arg & TXTNOBGCOLOUR)) {
		    txtchangeset(txtchangep, arg & TXT_ATTR_BG_ON_MASK,
				 TXTNOBGCOLOUR);
		    txtset(arg & TXT_ATTR_BG_ON_MASK);
		    set_colour_attribute(arg, COL_SEQ_BG, TSC_PROMPT);
		    break;
		}
		/* else FALLTHROUGH */
	    case 'k':
		txtchangeset(txtchangep, TXTNOBGCOLOUR, TXT_ATTR_BG_ON_MASK);
		txtunset(TXT_ATTR_BG_ON_MASK);
		set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, TSC_PROMPT);
		break;
	    case '[':
		if (idigit(*++bv->fm))
		    arg = zstrtol(bv->fm, &bv->fm, 10);
		if (!prompttrunc(arg, ']', doprint, endchar, txtchangep))
		    return *bv->fm;
		break;
	    case '<':
	    case '>':
		/* Test (minus) here so -0 means "at the right margin" */
		if (minus) {
		    *bv->bp = '\0';
		    countprompt(bv->bufline, &t0, 0, 0);
		    arg = zterm_columns - t0 + arg;
		    if (arg <= 0)
			arg = 1;
		}
		if (!prompttrunc(arg, *bv->fm, doprint, endchar, txtchangep))
		    return *bv->fm;
		break;
	    case '{': /*}*/
		if (!bv->dontcount++) {
		    addbufspc(1);
		    *bv->bp++ = Inpar;
		}
		if (arg <= 0)
		    break;
		/* else */
		/* FALLTHROUGH */
	    case 'G':
		if (arg > 0) {
		    addbufspc(arg);
		    while (arg--)
			*bv->bp++ = Nularg;
		} else {
		    addbufspc(1);
		    *bv->bp++ = Nularg;
		}
		break;
	    case /*{*/ '}':
		if (bv->trunccount && bv->trunccount >= bv->dontcount)
		    return *bv->fm;
		if (bv->dontcount && !--bv->dontcount) {
		    addbufspc(1);
		    *bv->bp++ = Outpar;
		}
		break;
	    case 't':
	    case '@':
	    case 'T':
	    case '*':
	    case 'w':
	    case 'W':
	    case 'D':
		{
		    char *tmfmt, *dd, *tmbuf = NULL;

		    switch (*bv->fm) {
		    case 'T':
			tmfmt = "%K:%M";
			break;
		    case '*':
			tmfmt = "%K:%M:%S";
			break;
		    case 'w':
			tmfmt = "%a %f";
			break;
		    case 'W':
			tmfmt = "%m/%d/%y";
			break;
		    case 'D':
			if (bv->fm[1] == '{' /*}*/) {
			    for (ss = bv->fm + 2; *ss && *ss != /*{*/ '}'; ss++)
				if(*ss == '\\' && ss[1])
				    ss++;
			    dd = tmfmt = tmbuf = zalloc(ss - bv->fm);
			    for (ss = bv->fm + 2; *ss && *ss != /*{*/ '}';
				 ss++) {
				if(*ss == '\\' && ss[1])
				    ss++;
				*dd++ = *ss;
			    }
			    *dd = 0;
			    bv->fm = ss - !*ss;
			    if (!*tmfmt) {
				free(tmbuf);
				continue;
			    }
			} else
			    tmfmt = "%y-%m-%d";
			break;
		    default:
			tmfmt = "%l:%M%p";
			break;
		    }
		    gettimeofday(&tv, &dummy_tz);
		    tm = localtime(&tv.tv_sec);
		    /*
		     * Hack because strftime won't say how
		     * much space it actually needs.  Try to add it
		     * a few times until it works.  Some formats don't
		     * actually have a length, so we could go on for
		     * ever.
		     */
		    for(j = 0, t0 = strlen(tmfmt)*8; j < 3; j++, t0*=2) {
			addbufspc(t0);
			if (ztrftime(bv->bp, t0, tmfmt, tm, tv.tv_usec) >= 0)
			    break;
		    }
		    /* There is enough room for this because addbufspc(t0)
		     * allocates room for t0 * 2 bytes. */
		    metafy(bv->bp, -1, META_NOALLOC);
		    bv->bp += strlen(bv->bp);
		    zsfree(tmbuf);
		    break;
		}
	    case 'n':
		stradd(get_username());
		break;
	    case 'l':
		if (*ttystrname) {
                   ss = (strncmp(ttystrname, "/dev/tty", 8) ?
                           ttystrname + 5 : ttystrname + 8);
		    stradd(ss);
		} else
		    stradd("()");
		break;
	    case 'y':
		if (*ttystrname) {
		    ss = (strncmp(ttystrname, "/dev/", 5) ?
			    ttystrname : ttystrname + 5);
		    stradd(ss);
		} else
		    stradd("()");
		break;
	    case 'L':
		addbufspc(DIGBUFSIZE);
#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
		sprintf(bv->bp, "%lld", shlvl);
#else
		sprintf(bv->bp, "%ld", (long)shlvl);
#endif
		bv->bp += strlen(bv->bp);
		break;
	    case '?':
		addbufspc(DIGBUFSIZE);
#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
		sprintf(bv->bp, "%lld", lastval);
#else
		sprintf(bv->bp, "%ld", (long)lastval);
#endif
		bv->bp += strlen(bv->bp);
		break;
	    case '%':
	    case ')':
		addbufspc(1);
		*bv->bp++ = *bv->fm;
		break;
	    case '#':
		addbufspc(1);
		*bv->bp++ = privasserted() ? '#' : '%';
		break;
	    case 'v':
		if (!arg)
		    arg = 1;
		else if (arg < 0)
		    arg += arrlen(psvar) + 1;
		if (arg > 0 && arrlen(psvar) >= arg)
		    stradd(psvar[arg - 1]);
		break;
	    case 'E':
                tsetcap(TCCLEAREOL, TSC_PROMPT);
		break;
	    case '^':
		if (cmdsp) {
		    if (arg >= 0) {
			if (arg > cmdsp || arg == 0)
			    arg = cmdsp;
			for (t0 = cmdsp - 1; arg--; t0--) {
			    stradd(cmdnames[cmdstack[t0]]);
			    if (arg) {
				addbufspc(1);
				*bv->bp++=' ';
			    }
			}
		    } else {
			arg = -arg;
			if (arg > cmdsp)
			    arg = cmdsp;
			for (t0 = arg - 1; arg--; t0--) {
			    stradd(cmdnames[cmdstack[t0]]);
			    if (arg) {
				addbufspc(1);
				*bv->bp++=' ';
			    }
			}
		    }
		}
		break;
	    case '_':
		if (cmdsp) {
		    if (arg >= 0) {
			if (arg > cmdsp || arg == 0)
			    arg = cmdsp;
			for (t0 = cmdsp - arg; arg--; t0++) {
			    stradd(cmdnames[cmdstack[t0]]);
			    if (arg) {
				addbufspc(1);
				*bv->bp++=' ';
			    }
			}
		    } else {
			arg = -arg;
			if (arg > cmdsp)
			    arg = cmdsp;
			for (t0 = 0; arg--; t0++) {
			    stradd(cmdnames[cmdstack[t0]]);
			    if (arg) {
				addbufspc(1);
				*bv->bp++=' ';
			    }
			}
		    }
		}
		break;
	    case 'r':
		if(bv->rstring)
		    stradd(bv->rstring);
		break;
	    case 'R':
		if(bv->Rstring)
		    stradd(bv->Rstring);
		break;
	    case 'e':
	    {
		int depth = 0;
		Funcstack fsptr = funcstack;
		while (fsptr) {
		    depth++;
		    fsptr = fsptr->prev;
		}
		addbufspc(DIGBUFSIZE);
		sprintf(bv->bp, "%d", depth);
		bv->bp += strlen(bv->bp);
		break;
	    }
	    case 'I':
		if (funcstack && funcstack->tp != FS_SOURCE &&
		    !IN_EVAL_TRAP()) {
		    /*
		     * We're in a function or an eval with
		     * EVALLINENO.  Calculate the line number in
		     * the file.
		     */
		    zlong flineno = lineno + funcstack->flineno;
		    /* take account of eval line nos. starting at 1 */
		    if (funcstack->tp == FS_EVAL)
			lineno--;
		    addbufspc(DIGBUFSIZE);
#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
		    sprintf(bv->bp, "%lld", flineno);
#else
		    sprintf(bv->bp, "%ld", (long)flineno);
#endif
		    bv->bp += strlen(bv->bp);
		    break;
		}
		/* else we're in a file and lineno is already correct */
		/* FALLTHROUGH */
	    case 'i':
		addbufspc(DIGBUFSIZE);
#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
		sprintf(bv->bp, "%lld", lineno);
#else
		sprintf(bv->bp, "%ld", (long)lineno);
#endif
		bv->bp += strlen(bv->bp);
		break;
	    case 'x':
		if (funcstack && funcstack->tp != FS_SOURCE &&
		    !IN_EVAL_TRAP())
		    promptpath(funcstack->filename ? funcstack->filename : "",
			       arg, 0);
		else
		    promptpath(scriptfilename ? scriptfilename : argzero,
			       arg, 0);
		break;
	    case '\0':
		return 0;
	    case Meta:
		bv->fm++;
		break;
	    }
	} else if(*bv->fm == '!' && isset(PROMPTBANG)) {
	    if(doprint) {
		if(bv->fm[1] == '!') {
		    bv->fm++;
		    addbufspc(1);
		    pputc('!');
		} else {
		    addbufspc(DIGBUFSIZE);
		    convbase(bv->bp, curhist, 10);
		    bv->bp += strlen(bv->bp);
		}
	    }
	} else {
	    char c = *bv->fm == Meta ? *++bv->fm ^ 32 : *bv->fm;

	    if (doprint) {
		addbufspc(1);
		pputc(c);
	    }
	}
    }

    return *bv->fm;
}
Exemple #5
0
static int
bin_sched(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
{
    char *s, **argptr;
    time_t t;
    long h, m, sec;
    struct tm *tm;
    struct schedcmd *sch, *sch2, *schl;
    int sn, flags = 0;

    /* If the argument begins with a -, remove the specified item from the
    schedule. */
    for (argptr = argv; *argptr && **argptr == '-'; argptr++) {
	char *arg = *argptr + 1;
	if (idigit(*arg)) {
	    sn = atoi(arg);

	    if (!sn) {
		zwarnnam("sched", "usage for delete: sched -<item#>.");
		return 1;
	    }
	    for (schl = NULL, sch = schedcmds, sn--;
		 sch && sn; sch = (schl = sch)->next, sn--);
	    if (!sch) {
		zwarnnam("sched", "not that many entries");
		return 1;
	    }
	    if (schl)
		schl->next = sch->next;
	    else {
		scheddeltimed();
		schedcmds = sch->next;
		if (schedcmds) {
		    DPUTS(timedfns && firstnode(timedfns), "BUG: already timed fn (2)");
		    schedaddtimed(schedcmds->time);
		}
	    }
	    zsfree(sch->cmd);
	    zfree(sch, sizeof(struct schedcmd));

	    return 0;
	} else if (*arg == '-') {
	    /* end of options */
	    argptr++;
	    break;
	} else if (!strcmp(arg, "o")) {
	    flags |= SCHEDFLAG_TRASH_ZLE;
	} else {
	    if (*arg)
		zwarnnam(nam, "bad option: -%c", *arg);
	    else
		zwarnnam(nam, "option expected");
	    return 1;
	}
    }

    /* given no arguments, display the schedule list */
    if (!*argptr) {
	for (sn = 1, sch = schedcmds; sch; sch = sch->next, sn++) {
	    char tbuf[60], *flagstr, *endstr;
	    time_t t;
	    struct tm *tmp;

	    t = sch->time;
	    tmp = localtime(&t);
	    ztrftime(tbuf, 40, "%a %b %e %k:%M:%S", tmp, 0L);
	    if (sch->flags & SCHEDFLAG_TRASH_ZLE)
		flagstr = "-o ";
	    else
		flagstr = "";
	    if (*sch->cmd == '-')
		endstr = "-- ";
	    else
		endstr = "";
	    printf("%3d %s %s%s%s\n", sn, tbuf, flagstr, endstr, sch->cmd);
	}
	return 0;
    } else if (!argptr[1]) {
	/* other than the two cases above, sched *
	 *requires at least two arguments        */
	zwarnnam("sched", "not enough arguments");
	return 1;
    }

    /* The first argument specifies the time to schedule the command for.  The
    remaining arguments form the command. */
    s = *argptr++;
    if (*s == '+') {
	/*
	 * + introduces a relative time.  The rest of the argument may be an
	 * hour:minute offset from the current time.  Once the hour and minute
	 * numbers have been extracted, and the format verified, the resulting
	 * offset is simply added to the current time.
	 */
	zlong zl = zstrtol(s + 1, &s, 10);
	if (*s == ':') {
	    m = (long)zstrtol(s + 1, &s, 10);
	    if (*s == ':')
		sec = (long)zstrtol(s + 1, &s, 10);
	    else
		sec = 0;
	    if (*s) {
		zwarnnam("sched", "bad time specifier");
		return 1;
	    }
	    t = time(NULL) + (long)zl * 3600 + m * 60 + sec;
	} else if (!*s) {
	    /*
	     * Alternatively, it may simply be a number of seconds.
	     * This is here for consistency with absolute times.
	     */
	    t = time(NULL) + (time_t)zl;
	} else {
	    zwarnnam("sched", "bad time specifier");
	    return 1;
	}
    } else {
	/*
	 * If there is no +, an absolute time must have been given.
	 * This may be in hour:minute format, optionally followed by a string
	 * starting with `a' or `p' (for a.m. or p.m.).  Characters after the
	 * `a' or `p' are ignored.
	 */
	zlong zl = zstrtol(s, &s, 10);
	if (*s == ':') {
	    h = (long)zl;
	    m = (long)zstrtol(s + 1, &s, 10);
	    if (*s == ':')
		sec = (long)zstrtol(s + 1, &s, 10);
	    else
		sec = 0;
	    if (*s && *s != 'a' && *s != 'A' && *s != 'p' && *s != 'P') {
		zwarnnam("sched", "bad time specifier");
		return 1;
	    }
	    t = time(NULL);
	    tm = localtime(&t);
	    t -= tm->tm_sec + tm->tm_min * 60 + tm->tm_hour * 3600;
	    if (*s == 'p' || *s == 'P')
		h += 12;
	    t += h * 3600 + m * 60 + sec;
	    /*
	     * If the specified time is before the current time, it must refer
	     * to tomorrow.
	     */
	    if (t < time(NULL))
		t += 3600 * 24;
	} else if (!*s) {
	    /*
	     * Otherwise, it must be a raw time specifier.
	     */
	    t = (long)zl;
	} else {
	    zwarnnam("sched", "bad time specifier");
	    return 1;
	}
    }
    /* The time has been calculated; now add the new entry to the linked list
    of scheduled commands. */
    sch = (struct schedcmd *) zalloc(sizeof *sch);
    sch->time = t;
    sch->cmd = zjoin(argptr, ' ', 0);
    sch->flags = flags;
    /* Insert into list in time order */
    if (schedcmds) {
	if (sch->time < schedcmds->time) {
	    scheddeltimed();
	    sch->next = schedcmds;
	    schedcmds = sch;
	    DPUTS(timedfns && firstnode(timedfns), "BUG: already timed fn (3)");
	    schedaddtimed(t);
	} else {
	    for (sch2 = schedcmds;
		 sch2->next && sch2->next->time < sch->time;
		 sch2 = sch2->next)
		;
	    sch->next = sch2->next;
	    sch2->next = sch;
	}
    } else {
	sch->next = NULL;
	schedcmds = sch;
	DPUTS(timedfns && firstnode(timedfns), "BUG: already timed fn (4)");
	schedaddtimed(t);
    }
    return 0;
}