|
|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - download
Length: 11769 (0x2df9)
Types: TextFile
Notes: UNIX file
Names: »at.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
└─⟦f4b8d8c84⟧ UNIX Filesystem
└─⟦this⟧ »cmd/at.c«
/*
* At takes a list of commands and arranges for them to be executed at
* a specified time. When the commands are executed, the user id, group
* id, exported shell variables and current directory will all be as they
* were when at was executed.
* The format of the at command is
* at [-v] [-c command] time [week] [file]
* or
* at [-v] [-c command] time day_of_week [week] [file]
* or
* at [-v] [-c command] time month day_of_month [file]
* Here the presence of `week' implies that the command should occur
* on the next week. If the v-flag is specified, then at prints out
* when the command will be executed. If the c-flag is specified
* then the command come from the string `command'. If a file is
* specified, then they come from that file. If none of these is
* done, then standard input is read.
* Note that at should not be set uid or set gid. Anyone can write
* in the at spooling directory. Security is maintained because at
* sets the set uid, set gid and owner execute bits. Without all of
* these being set, atrun will not run the script.
*/
#include <stdio.h>
#include <ctype.h>
#include <types.h>
#include <time.h>
#include <dir.h>
#include <signal.h>
#define NUL '\0'
#define TRUE (0 == 0)
#define FALSE (0 != 0)
#define STDOUT 1
#define WEEK 7
#define YEAR 365
#define LEAPM 1 /* leap month */
#define YBASE 1900 /* Year bias in struct tm */
#define FSTDAY 0 /* Week day of 1st day of year 0 */
#define UNKNOWN -1
#define MODE 06500 /* set uid, set gid, owner read-exec */
#define OUTNL 13 /* "yymmddhhmm.aa" */
struct tm td = { /* time to do command */
0, UNKNOWN, UNKNOWN,
UNKNOWN, UNKNOWN, UNKNOWN,
UNKNOWN, UNKNOWN, UNKNOWN
};
int wflg = FALSE, /* `week' flag */
vflg = FALSE; /* verify flag */
char *cstr = NULL, /* c-flag command string */
outn[] = "/usr/spool/at/yymmddhhmm.aa";
FILE *outfp = NULL; /* file to place commands into */
extern char **environ;
main(argc, argv)
int argc;
char *argv[];
{
setsigs();
options(argv);
if (vflg)
printf("%s", asctime(&td));
makefile();
copyenv();
copyumask();
copywd();
copycmds();
return (0);
}
/*
* Setsigs simply assures that on an interrupt we will clean up after
* ourselves so as to avoid leaving junk in at's spooling directory.
*/
setsigs()
{
register int (*fnc)();
int (*signal())(),
clean();
fnc = signal(SIGINT, SIG_IGN);
if (fnc != SIG_IGN)
signal(SIGINT, clean);
}
/*
* Clean is called when an interrupt occurs, or on any error. Its only
* function is to unlink the partial file in at's spooling directory.
*/
clean()
{
if (outfp != NULL) {
fclose(outfp);
unlink(outn);
}
exit(1);
}
/*
* Makefile makes a file in the correct directory with a name of the
* form
* yymmddhhmm.xx
* where
* yy is the year (mod 100)
* mm is the month
* dd is the day of the month
* hh is the hour
* and
* mm is the minute
* at which the command is to be execute. The xx is simply to make
* the file name unique. The name is placed in outn and the stream
* is in outfp. Also, the mode of the file is execute for owner only.
* An initial line is written on the file to remove itself.
*/
makefile()
{
register char *cp;
cp = &outn[(sizeof outn) - OUTNL - 1];
sprintf(cp, "%02d%02d%02d%02d%02d.aa", td.tm_year % 100,
1 + td.tm_mon, td.tm_mday, td.tm_hour, td.tm_min);
cp = &outn[(sizeof outn) - 2];
while (access(outn, 0) == 0)
if (++*cp > 'z') {
*cp = 'a';
if (++cp[-1] > 'z')
die("Too many things to do at the same time");
}
outfp = fopen(outn, "w");
if (outfp == NULL)
die("Can't create %s\n", outn);
chmod(outn, MODE);
fprintf(outfp, "rm -f %s\n", outn);
}
/*
* Copyenv writes onto outfp a pair of lines for each item in the environment.
* These lines are of the form
* var=value
* export var
* This will cause all exported variables to be reset when the file
* is executed by the shell.
*/
copyenv()
{
register char **vp,
*cp,
ch;
char *end;
for (vp=environ; (cp=*vp) != NULL; ++vp) {
while ((ch=*cp++) != '=' && ch != NUL)
putc(ch, outfp);
end = cp - 1;
fprintf(outfp, "='");
if (ch != '=')
--cp;
while ((ch=*cp++) != NUL)
if (ch == '\'')
fprintf(outfp, "'\\''");
else
putc(ch, outfp);
fprintf(outfp, "'\n");
fprintf(outfp, "export %.*s\n", end-*vp, *vp);
}
}
/*
* Copyumask writes out a line onto the stream outfp of the form
* umask number
* where number is the current umask. This will cause the shell
* to execute the commands with the current umask.
*/
copyumask()
{
register int um;
um = umask(0777);
umask(um);
fprintf(outfp, "umask %03o\n", um);
}
/*
* Copywd writes a line onto the file outfp of the form
* cd current working directory
* This will cause the current directory to be reset when the file
* is executed by the shell.
*/
copywd()
{
register int pid;
int stat;
static char *args[] = {
"pwd",
NULL
};
fprintf(outfp, "cd ");
fflush(outfp);
pid = fork();
if (pid == 0) {
dup2(fileno(outfp), STDOUT);
fclose(outfp);
execv("/bin/pwd", args);
execv("/usr/bin/pwd", args);
exit(1);
}
if (pid < 0)
die("Try again");
wait(&stat);
if (stat != 0)
die("Can't find pwd");
}
/*
* Copycmds simply copies from stdin until EOF.
*/
copycmds()
{
register int ch;
if (cstr != NULL)
fprintf(outfp, "%s\n", cstr);
else
while ((ch=getchar()) != EOF)
putc(ch, outfp);
}
/*
* Options cracks the command line options.
*/
options(argv)
register char *argv[];
{
char **mopts(),
**getdate();
argv = mopts(++argv);
if (*argv == NULL)
usage();
gettime(*argv++);
argv = mopts(argv);
argv = getdate(argv);
argv = mopts(argv);
if (*argv != NULL) {
if (cstr != NULL)
usage();
if (freopen(*argv, "r", stdin) == NULL)
die("Can't open %s", *argv);
argv = mopts(++argv);
}
if (*argv != NULL)
usage();
cyday();
}
/*
* Mopts cracks the `minus' options. It returns the first
* unused argument.
*/
char **
mopts(argv)
register char *argv[];
{
register char *str,
ch;
for (str=*argv; str != NULL && *str++ == '-'; str=*++argv)
for (ch=*str++; ch != NUL; ch=*str++)
switch (ch) {
case 'c':
if ((cstr = *++argv) == NULL)
usage();
break;
case 'v':
vflg = TRUE;
break;
default:
usage();
}
return (argv);
}
/*
* Usage give the usage message and dies.
*/
usage()
{
static char umsg[] =
"usage: at [-v] [-c command] time [month month_day | week_day] [file]";
die(umsg);
}
/*
* Die prints a message on stderr and exits with status one.
*/
die(str)
char *str;
{
fprintf(stderr, "%r\n", &str);
clean();
}
/*
* Gettime takes the string `str' and sets the td minute and
* hour fields appropriately. A time is:
* either
* 0-2 digits (hours)
* or
* 3-4 digits (hours and minutes)
* possibly followed by a string starting with
* a for A.M.
* p for P.M.
* n for noon
* m for mid-night.
*/
gettime(str)
char *str;
{
register int ch,
hour;
int min;
char *chp;
hour = 0;
chp = str;
for (ch=*chp++; isascii(ch) && isdigit(ch); ch = *chp++)
hour = 10*hour + ch - '0';
if (chp - str - 1 <= 2)
hour *= 100;
min = hour % 100;
hour /= 100;
if (hour >= 24 || min >= 60)
usage();
switch (ch) {
case NUL:
break;
case 'a':
if (hour == 12)
hour = 0;
break;
case 'p':
if (hour < 12)
hour += 12;
break;
case 'n':
hour = 12;
break;
case 'm':
hour = 0;
break;
default:
usage();
}
td.tm_hour = hour;
td.tm_min = min;
}
/*
* Getdate fills in either the month and month-day or the week-day
* fields of td from the date pointed to by `argv'. It returns
* a pointer to the next argument.
*/
char **
getdate(argv)
register char *argv[];
{
register int idx;
static char *day[] = {
"sunday", "monday",
"tuesday", "wednesday",
"thursday", "friday",
"saturday", NULL
},
*month[] = {
"january", "february",
"march", "april",
"may", "june",
"july", "august",
"september", "october",
"november", "december",
NULL
};
if (*argv == NULL)
return (argv);
idx = mut(*argv, month);
if (idx != EOF) {
td.tm_mon = idx;
if (*++argv == NULL || (td.tm_mday=atoi(*argv++)) == 0)
usage();
return (argv);
}
idx = mut(*argv, day);
if (idx != EOF) {
td.tm_wday = idx;
++argv;
}
if (*argv != NULL && (wflg = strcmp(*argv, "week")==0))
++argv;
return (argv);
}
/*
* Mut searches the table `tbl' for entrys which may be trunctated to
* `str'. If there is exactly one such entry, it returns its ordinal.
* Otherwise it returns EOF. Note that `tbl' should be NULL-terminated.
*/
mut(str, tbl)
register char *str;
char *tbl[];
{
register char **tp;
register int len;
int res;
tp = tbl;
len = strlen(str);
do {
if (*tp == NULL)
return (EOF);
} while (strncmp(*tp++, str, len) != 0);
res = tp - tbl - 1;
do {
if (*tp == NULL)
return (res);
} while (strncmp(*tp++, str, len) != 0);
return (EOF);
}
/*
* Cyday computes the year and year-day on which the command should be
* performed. It does this on the basis of wflg (which is true iff
* we should delay by a week) and td.
* The result is placed in td.
*/
cyday()
{
register int late,
len;
register struct tm *ct; /* current time */
time_t now;
time(&now);
ct = localtime(&now);
td.tm_yday = ct->tm_yday;
td.tm_year = ct->tm_year;
if (td.tm_mon != UNKNOWN) {
late = td.tm_mon <= ct->tm_mon;
late &= td.tm_mon < ct->tm_mon
|| td.tm_mday < ct->tm_mday;
if (late)
++td.tm_year;
td.tm_yday = mdtoyd(td.tm_year + YBASE, td.tm_mon, td.tm_mday);
td.tm_wday = ydtowd(td.tm_year + YBASE, td.tm_yday);
} else {
late = td.tm_hour <= ct->tm_hour;
late &= td.tm_hour < ct->tm_hour
|| td.tm_min <= ct->tm_min;
if (td.tm_wday != UNKNOWN) {
td.tm_yday += (td.tm_wday + WEEK - ct->tm_wday) % WEEK;
late &= td.tm_wday == ct->tm_wday;
if (late | wflg)
td.tm_yday += WEEK;
} else {
td.tm_wday = ct->tm_wday;
if (wflg)
td.tm_yday += WEEK;
else if (late) {
++td.tm_yday;
td.tm_wday = (td.tm_wday+1) % WEEK;
}
}
len = (isleap(td.tm_year + YBASE)) ? YEAR+1 : YEAR;
if (td.tm_yday >= len) {
td.tm_yday -= len;
++td.tm_year;
}
ydtom(td.tm_year + YBASE, td.tm_yday, &td.tm_mon, &td.tm_mday);
}
}
/*
* Isleap returns TRUE iff the year `year' is a leap year.
*/
isleap(year)
register int year;
{
return (year%4 == 0 && (year%100 != 0 || year%400 == 0));
}
/*
* The array mlen is an array of month lengths, indexed by the
* month number (0 - 11).
*/
static int mlen[] = { /* month lengths */
31, 28, 31, 30,
31, 30, 31, 31,
30, 31, 30, 31
};
/*
* Mdtoyd returns the year-day for the date with year `year', month
* `month' and month-day `mday'.
*/
mdtoyd(year, month, mday)
register int month;
int year,
mday;
{
register int res,
leap;
res = mday - 1;
if (leap = isleap(year))
++mlen[LEAPM];
while (--month >= 0)
res += mlen[month];
if (leap)
--mlen[LEAPM];
return (res);
}
/*
* Ydtowd returns the week day for the date with year `year' and
* year-day `yday'.
*/
ydtowd(year, yday)
register int year;
int yday;
{
register int wday;
--year;
wday = FSTDAY + year * (YEAR%WEEK) + yday%WEEK;
wday += (year+4) / 4;
wday -= (year+100) / 100;
wday += (year+400) / 400;
wday %= WEEK;
return (wday);
}
/*
* Ydtom sets `pmonth' and `pmday' to the month and month-day of the
* date with year `year' and year-day `yday'.
*/
ydtom(year, yday, pmonth, pmday)
int year,
yday,
*pmonth,
*pmday;
{
register int mday,
month;
int leap;
if (leap = isleap(year))
++mlen[LEAPM];
mday = yday;
for (month=0; mday >= mlen[month]; ++month)
mday -= mlen[month];
*pmonth = month;
*pmday = mday + 1;
if (leap)
--mlen[LEAPM];
}