From a8b669200aac9ab421579a5e3ea36c8974b4c3c9 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 16 May 2016 15:43:17 +0100 Subject: [PATCH] Import first commands from the freed up MwC Coherent sources --- Applications/MWC/COPYRIGHT | 29 + Applications/MWC/cmd/Makefile | 56 ++ Applications/MWC/cmd/Makefile.6809 | 40 + Applications/MWC/cmd/ac.c | 324 ++++++++ Applications/MWC/cmd/almanac.c | 92 +++ Applications/MWC/cmd/at.c | 556 ++++++++++++++ Applications/MWC/cmd/calendar.c | 471 ++++++++++++ Applications/MWC/cmd/col.c | 599 +++++++++++++++ Applications/MWC/cmd/deroff.c | 507 +++++++++++++ Applications/MWC/cmd/m4.c | 1020 +++++++++++++++++++++++++ Applications/MWC/cmd/moo.c | 87 +++ Applications/MWC/cmd/pnmatch.c | 85 +++ Applications/MWC/cmd/pr.c | 458 ++++++++++++ Applications/MWC/cmd/tar.c | 1108 ++++++++++++++++++++++++++++ Applications/MWC/cmd/test.c | 627 ++++++++++++++++ Applications/MWC/cmd/ttt.c | 274 +++++++ 16 files changed, 6333 insertions(+) create mode 100644 Applications/MWC/COPYRIGHT create mode 100644 Applications/MWC/cmd/Makefile create mode 100644 Applications/MWC/cmd/Makefile.6809 create mode 100644 Applications/MWC/cmd/ac.c create mode 100644 Applications/MWC/cmd/almanac.c create mode 100644 Applications/MWC/cmd/at.c create mode 100644 Applications/MWC/cmd/calendar.c create mode 100644 Applications/MWC/cmd/col.c create mode 100644 Applications/MWC/cmd/deroff.c create mode 100644 Applications/MWC/cmd/m4.c create mode 100644 Applications/MWC/cmd/moo.c create mode 100644 Applications/MWC/cmd/pnmatch.c create mode 100755 Applications/MWC/cmd/pr.c create mode 100644 Applications/MWC/cmd/tar.c create mode 100755 Applications/MWC/cmd/test.c create mode 100644 Applications/MWC/cmd/ttt.c diff --git a/Applications/MWC/COPYRIGHT b/Applications/MWC/COPYRIGHT new file mode 100644 index 00000000..8346458c --- /dev/null +++ b/Applications/MWC/COPYRIGHT @@ -0,0 +1,29 @@ +License + +Copyright © 1977-1995 by Robert Swartz. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +This software is provided by the copyright holders and contributors "as is" and +any express or implied warranties, including, but not limited to, the implied +warranties of merchantability and fitness for a particular purpose are +disclaimed. In no event shall the copyright holder or contributors be liable for +any direct, indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or services; +loss of use, data, or profits; or business interruption) however caused and on +any theory of liability, whether in contract, strict liability, or tort +(including negligence or otherwise) arising in any way out of the use of this +software, even if advised of the possibility of such damage. diff --git a/Applications/MWC/cmd/Makefile b/Applications/MWC/cmd/Makefile new file mode 100644 index 00000000..c6949471 --- /dev/null +++ b/Applications/MWC/cmd/Makefile @@ -0,0 +1,56 @@ +CC = sdcc +ASM = sdasz80 +AR = sdar +LINKER = sdcc +FCC = ../../../Library/tools/fcc +FCCOPTS = -O2 +PLATFORM = +#PLATFORM = -tzx128 + +PROGLOAD=`(cat ../../Kernel/platform/config.h; echo PROGLOAD) | cpp -E | tail -n1` + +.SUFFIXES: .c .rel + +SRCSNS = test.c + +SRCS = ac.c almanac.c at.c col.c deroff.c moo.c pr.c tar.c ttt.c + +SRCSBAD = calendar.c m4.c + +OBJS = $(SRCS:.c=.rel) +OBJSNS = $(SRCSNS:.c=.rel) +OBJSBAD = $(SRCSBAD:.c=.rel) + +LIBS = ../../../Library/libs/syslib.lib + +APPSNS = $(OBJSNS:.rel=) + +APPS = $(OBJS:.rel=) $(OBJSBAD:.rel=) $(OBJSNS:.rel=) + +all: $(APPS) sizes + + +$(APPSNS): OPTS = --nostdio + +$(OBJS): %.rel: %.c + +$(OBJSNS): %.rel: %.c + +$(OBJSBAD): %.rel: %.c + $(FCC) $(PLATFORM) -c $< + +.c.rel: + $(FCC) $(PLATFORM) $(FCCOPTS) -c $< + +%: %.rel + $(FCC) $(PLATFORM) $(OPTS) $< -o $@ + +sizes: $(APPS) + ls -l $(APPS) >size.report + +clean: + rm -f $(OBJS) $(APPS) $(SRCS:.c=) core *~ *.asm *.lst *.sym *.map *.noi *.lk *.ihx *.tmp *.bin size.report + +rmbak: + rm -f *~ core + diff --git a/Applications/MWC/cmd/Makefile.6809 b/Applications/MWC/cmd/Makefile.6809 new file mode 100644 index 00000000..68400cfb --- /dev/null +++ b/Applications/MWC/cmd/Makefile.6809 @@ -0,0 +1,40 @@ +PLATFORM = 6809 +CC = m6809-unknown-gcc +# These are wrappers for lwasm and lwar +ASM = m6809-unknown-as +AR = m6809-unknown-ar +LINKER = lwlink +CFLAGS = -I../../../Library/include -I../../../Library/include/6502 -Wall -pedantic +LINKER_OPT = --format=raw -L../../../Library/libs -lc6809 +LIBGCCDIR = $(dir $(shell $(CC) -print-libgcc-file-name)) +LINKER_OPT += -L$(LIBGCCDIR) -lgcc +LINKER_OPT += --script=../../util/$(TARGET).link +ASM_OPT = -o +CRT0 = ../../../Library/libs/crt0_6809.o + +.SUFFIXES: .c .o + + +SRCS = ac.c almanac.c at.c calendar.c col.c deroff.c m4.c moo.c pr.c tar.c test.c ttt.c + +OBJS = $(SRCS:.c=.o) + +APPS = $(OBJS:.o=) + +all: $(APPS) sizes + +$(OBJS): $(SRCS) + +$(APPS): $(CRT0) + +%: %.o + $(LINKER) -o $@ $(LINKER_OPT) $(CRT0) $< + +sizes: $(APPS) + ls -l $(APPS) > size.report + +clean: + rm -f $(OBJS) $(APPS) $(SRCS:.c=) core *~ *.asm *.lst *.sym *.map *.noi *.lk *.ihx *.tmp *.bin size.report + +rmbak: + rm -f *~ core diff --git a/Applications/MWC/cmd/ac.c b/Applications/MWC/cmd/ac.c new file mode 100644 index 00000000..37f39a40 --- /dev/null +++ b/Applications/MWC/cmd/ac.c @@ -0,0 +1,324 @@ +/* + * AC + * Login connect-time accounting. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DAYSEC (24*60*60L) /* Seconds in a day */ +#define HOUR (60*60) /* Seconds in hour */ +#define DIRSIZ 30 /* FIXME: use shorter init size anyway */ + +char *wtmpf = "/var/log/wtmp"; + +char *months[] = { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December", +}; + +char *days[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" +}; +char **plist; /* List of do-only people */ + +typedef struct TERM { + struct TERM *t_next; + char t_line[8]; + char t_name[DIRSIZ]; + time_t t_time; +} TERM; + +TERM *terminals; + +typedef struct PEOPLE { + struct PEOPLE *p_next; + char p_name[DIRSIZ]; + time_t p_time; +} PEOPLE; + +PEOPLE *people; + +time_t lasttime; /* Last time read from file */ +time_t runtotal; /* Running total used by ac -d */ +time_t midnight; /* Time of next midnight */ + +int dflag; /* Daily version (midnight-midnight) */ +int pflag; /* Print totals by people */ +int badflag; /* Possible bad file format */ + +/* + * Print a time entry with a name. + */ +void prtime(const char *name, time_t time) +{ + if (dflag) + printf("\t"); + printf("%-*.*s", DIRSIZ, DIRSIZ, name); + time = (time+30)/60; + printf("%3ld:%02ld\n", time/60, time%60); +} + +/* + * Print the results, depending on the flags. + * The `flag' says whether print is called at the + * end (0) or for each midnight-midnight period (1). + */ +void print(int flag) +{ + struct tm *tmp; + PEOPLE *pp; + time_t total = 0; + + if (dflag) { + time_t endofday; + + endofday = midnight-2*HOUR; + tmp = localtime(&endofday); + printf("%s %s %d:\n", days[tmp->tm_wday], months[tmp->tm_mon], + tmp->tm_mday); + } + for (pp = people; pp != NULL; pp = pp->p_next) { + if (pp->p_time > 0) { + total += pp->p_time; + if (pflag) + prtime(pp->p_name, pp->p_time); + } + if (flag) + pp->p_time = 0; + } + runtotal += total; + prtime("Total:", total); + if (dflag) { + printf("\n"); + if (flag == 0) { + dflag = 0; + prtime("Total:", runtotal); + } + } +} + +/* + * Enter a terminal node into the people table. + */ +void enter(TERM *tp) +{ + PEOPLE *pp; + + if (tp->t_name[0] == '\0') + return; + for (pp = people; pp != NULL; pp = pp->p_next) + if (strncmp(tp->t_name, pp->p_name, DIRSIZ) == 0) { + pp->p_time += tp->t_time; + return; + } + if ((pp = (PEOPLE *)malloc(sizeof(PEOPLE))) == NULL) + errx(1, "Out of memory for people"); + pp->p_next = people; + people = pp; + pp->p_time = tp->t_time; + strncpy(pp->p_name, tp->t_name, DIRSIZ); +} + +/* + * Log the times recorded for the terminals by + * midnight of the current day. + */ +void atmidnight(void) +{ + TERM *tp; + + for (tp = terminals; tp != NULL; tp = tp->t_next) { + if (tp->t_time < midnight) { + tp->t_time = midnight-tp->t_time; + enter(tp); + tp->t_time = midnight; + } + } +} + +/* + * Mark the user found on this terminal + * as logged in. + */ +void login(struct utmp *utp) +{ + TERM *tp; + char *np; + + for (np = utp->ut_user; *np != '\0'; np++) + if (!isascii(*np) || !isprint(*np)) { + badflag++; + lasttime = 0; + return; + } + for (np = utp->ut_line; *np != '\0'; np++) + if (!isascii(*np) || (!isalpha(*np) && !isdigit(*np))) { + badflag++; + lasttime = 0; + return; + } + if (*plist != NULL) { + char **plp; + + for (plp = plist; *plp != NULL; plp++) + if (**plp == *utp->ut_user + && strncmp(*plp, utp->ut_user, DIRSIZ)==0) + break; + if (*plp == NULL) + return; + } + if ((tp = (TERM*)malloc(sizeof(TERM)))==NULL) + errx(1, "Out of memory for terminals"); + tp->t_time = utp->ut_time; + strncpy(tp->t_name, utp->ut_user, DIRSIZ); + strncpy(tp->t_line, utp->ut_line, 8); + tp->t_next = terminals; + terminals = tp; +} + +/* + * Mark a user as logged out. + * The user is given by the tty-name + * found in the utmp structure pointer. + * If this pointer is NULL, log all users + * out (e.g. at reboot and end of file). + */ +void logout(struct utmp *utp) +{ + TERM *tp, *ptp; + +loop: + if (terminals == NULL) + return; + ptp = NULL; + for (tp=terminals; tp != NULL; ptp=tp, tp=tp->t_next) + if (utp==NULL || strncmp(tp->t_line, utp->ut_line, 8)==0) { + if (ptp == NULL) + terminals = tp->t_next; else + ptp->t_next = tp->t_next; + tp->t_time = (utp==NULL ? lasttime : utp->ut_time) + - tp->t_time; + enter(tp); + free((char *)tp); + if (utp != NULL) + return; + goto loop; + } +} + +/* + * Read through the wtmp file keeping track of + * each individual user. At the end either print total + * or by individual people. + */ +void readwtmp(void) +{ + struct utmp ut; + struct tm *tmp; + TERM *tp; + FILE *fp; + time_t tdelta = 0; + + if ((fp = fopen(wtmpf, "r")) == NULL) { + fprintf(stderr, "ac: cannot open %s\n", wtmpf); + exit(1); + } + while (fread(&ut, sizeof ut, 1, fp) == 1) { + if (dflag && midnight==0) { + tmp = localtime(&ut.ut_time); + midnight = ut.ut_time - tmp->tm_sec + - tmp->tm_min*60 - tmp->tm_hour*60*60L + DAYSEC; + } else while (dflag && lasttime>=midnight) { + atmidnight(); + print(1); + midnight += DAYSEC; + } + if (ut.ut_line[1] == '\0') + switch (ut.ut_line[0]) { + case '~': /* Reboot */ + lasttime = ut.ut_time; + logout(NULL); + continue; + + case '|': /* Old time */ + tdelta = ut.ut_time; + continue; + + case '}': /* New time */ + tdelta = ut.ut_time-tdelta; + lasttime = ut.ut_time; + for (tp = terminals; tp!=NULL; tp = tp->t_next) + tp->t_time += tdelta; + continue; + } + if (ut.ut_time < lasttime) { + if (lasttime-ut.ut_time > 60) { + badflag++; + continue; + } + ut.ut_time = lasttime; + } else + lasttime = ut.ut_time; + logout(&ut); + if (ut.ut_user[0] != '\0') + login(&ut); + } + fclose(fp); + time(&lasttime); + logout(NULL); +} + + +/* + * Error reporting. + */ +void usage(void) +{ + fprintf(stderr, "Usage: ac [-w wtmpfile] [-d] [-p] [username ...]\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + char *ap; + + while (argc>1 && *argv[1]=='-') { + for (ap = &argv[1][1]; *ap != '\0'; ap++) + switch (*ap) { + case 'd': + dflag = 1; + break; + + case 'p': + pflag = 1; + break; + + + case 'w': + if (argc < 2) + usage(); + wtmpf = argv[2]; + argv++; + argc--; + break; + + default: + usage(); + } + argv++; + argc--; + } + plist = argv+1; + readwtmp(); + print(0); + if (badflag) + errx(1, "possible bad file format"); + exit(0); +} diff --git a/Applications/MWC/cmd/almanac.c b/Applications/MWC/cmd/almanac.c new file mode 100644 index 00000000..5b58a968 --- /dev/null +++ b/Applications/MWC/cmd/almanac.c @@ -0,0 +1,92 @@ +/* + * Print an almanac of daily events. Program reads today's date, + * constructs a date string, and then examines the files + * /usr/games/lib/almanac.birth, /usr/games/lib/almanac.death, and + * /usr/games/lib/almanac.event for strings that contain the date string. + * Originally written in the shell. + * + * By fb, 7/12/88. + */ + +#include +#include +#include +#include +#include +#include + +#define EXIT_SUCCESS 0 +#define EXIT_FAILURE 1 + +const char *months[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +const char *filenames[] = { + "/usr/games/lib/almanac.birth", + "/usr/games/lib/almanac.death", + "/usr/games/lib/almanac.event" +}; + +const char *slugs[] = { + "BIRTHS:", + "DEATHS:", + "EVENTS:" +}; + +/* print an error message and exit */ +void fatal(const char *message) +{ + fprintf(stderr, "%s\n", message); + exit(EXIT_FAILURE); +} + +void findstring(const char *date, FILE * fileptr) +{ + char buffer[120]; + int length = strlen(date); + + while (fgets(buffer, 120, fileptr) != NULL) { + if (strncmp(buffer, date, length) == 0) + printf("%s", buffer + length); + else + continue; + } +} + +int main(int argc, char *argv[]) +{ + time_t rawtime; + struct tm *brokentime; + FILE *fileptr; + char buffer[20]; + int i; + + if (argc == 3) { + if (strlen(argv[1]) < 3) + fatal("Usage: almanac [Mon date]"); + *(argv[1] + 3) = '\0'; + sprintf(buffer, "%3s %s ", argv[1], argv[2]); + } else if (argc == 1) { + /* build the time string */ + rawtime = time(NULL); + brokentime = localtime(&rawtime); + sprintf(buffer, "%s %d ", months[brokentime->tm_mon], + brokentime->tm_mday); + } else + fatal("Usage: almanac [Mon date]"); + + /* open almanac text files; call string printing routine */ + for (i = 0; i < 3; i++) { + if ((fileptr = fopen(filenames[i], "r")) == NULL) + fatal("Cannot open almanac file."); + + printf("%s\n", slugs[i]); + + findstring(buffer, fileptr); + + fclose(fileptr); + } + exit(EXIT_SUCCESS); +} diff --git a/Applications/MWC/cmd/at.c b/Applications/MWC/cmd/at.c new file mode 100644 index 00000000..7bccab39 --- /dev/null +++ b/Applications/MWC/cmd/at.c @@ -0,0 +1,556 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#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; + +/* + * 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. + */ +void clean(int sig) +{ + if (outfp != NULL) { + fclose(outfp); + unlink(outn); + } + exit(1); +} + +/* + * Die prints a message on stderr and exits with status one. + */ +void die(const char *str, ...) +{ + va_list va; + va_start(va, str); + vwarnx(str, va); + va_end(va); + clean(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. + */ +void setsigs(void) +{ + sighandler_t fnc; + + fnc = signal(SIGINT, SIG_IGN); + if (fnc != SIG_IGN) + signal(SIGINT, clean); +} + +/* + * 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. + */ + +void makefile(void) +{ + 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. + */ + +void copyenv(void) +{ + char **vp, *cp, ch, *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. + */ +void copyumask(void) +{ + 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. + * + * FIXME: quoting ? + */ +void copywd(void) +{ + int pid; + int stat; + static const 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. + */ +void copycmds(void) +{ + int ch; + + if (cstr != NULL) + fprintf(outfp, "%s\n", cstr); + else + while ((ch=getchar()) != EOF) + putc(ch, outfp); +} + +/* + * Usage give the usage message and dies. + */ +void usage(void) +{ + static char umsg[] = +"usage: at [-v] [-c command] time [month month_day | week_day] [file]"; + + die(umsg); +} + +/* + * Mopts cracks the `minus' options. It returns the first + * unused argument. + */ + +char **mopts(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); +} + +/* + * 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. + */ +void gettime(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; +} + +/* + * 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. + */ +int mut(const char *str, const 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); +} + +/* + * 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(char *argv[]) +{ + register int idx; + static const 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); +} + +/* + * Isleap returns TRUE iff the year `year' is a leap year. + */ +int isleap(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'. + */ +int mdtoyd(int year, int month, int mday) +{ + 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'. + */ +int ydtowd(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'. + */ +int ydtom(int year, int yday, int *pmonth, int *pmday) +{ + 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]; +} + +/* + * 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. + */ +void cyday(void) +{ + int late, len; + 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); + } +} + +/* + * Options cracks the command line options. + */ + +void options(char *argv[]) +{ + 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(); +} + + +int main(int argc, char *argv[]) +{ + setsigs(); + options(argv); + if (vflg) + printf("%s", asctime(&td)); + makefile(); + copyenv(); + copyumask(); + copywd(); + copycmds(); + return (0); +} + diff --git a/Applications/MWC/cmd/calendar.c b/Applications/MWC/cmd/calendar.c new file mode 100644 index 00000000..becaadcc --- /dev/null +++ b/Applications/MWC/cmd/calendar.c @@ -0,0 +1,471 @@ +/* + * calendar + * Reminder utililty: read calendar files and lines with dates matching + * the current date or the date specified in the option string. + */ + +#include +#include +#include +#include +#include +#include +#include + +const char *message = +"Usage: calendar [ -a ] [ -ffile ]... [ -d[date] ] [ -w[date] ] [ -m[month] ]\n\ +Options:\n\ + -a Search calendars of all users and send mail.\n\ + -ffile Search each \"file\" in order given.\n\ + -d[date] Print all entries matching \"date\".\n\ + -w[date] Print entries in the week beginning with \"date\".\n\ + -m[month] Print entries in the given \"month\".\n\ +The default calendar is $HOME/.calendar. The default date is today.\n\ +"; + +char *argv0; +int all = 0; +int wday; +int advance; +char CurLine[512]; +char *CurLinep; + +enum {NONE, DAY, WEEK, MONTH} mflag; + +#define CALFILE "/.calendar" + +/* + * pnmatch(string, pattern, unanchored) + * returns 1 if pattern matches in string. + * pattern: + * [c1c2...cn-cm] class of characters. + * ? any character. + * * any # of any character. + * ^ beginning of string (if unanchored) + * $ end of string (if unanchored) + * unanch: + * 0 normal (anchored) pattern. + * 1 unanchored (^$ also metacharacters) + * >1 end unanchored. + * >1 is used internally but should not be used by the user. + */ + +int pnmatch(const char *s, const char *p, int unanch) +{ + int c1; + int c2; + + if (unanch == 1) { + while (*s) + if (pnmatch(s++, p, ++unanch)) + return (1); + return (0); + } + while ((c2 = *p++) != 0) { + c1 = *s++; + switch(c2) { + case '^': + if (unanch == 2) { + s--; + continue; + } else if (unanch == 0) + break; + else + return (0); + + case '$': + if (unanch) + return (c1 == '\0'); + break; + + case '[': + for (;;) { + c2 = *p++; + if (c2=='\0' || c2==']') + return (0); + if (c2 == '\\' && *p == '-') + c2 = *p++; + if (c2 == c1) + break; + if (*p == '-') + if (c1<=*++p && c1>=c2) + break; + } + while (*p && *p++!=']') + ; + + case '?': + if (c1) + continue; + return(0); + + case '*': + if (!*p) + return(1); + s--; + do { + if (pnmatch(s, p, unanch)) + return (1); + } while(*s++ != '\0'); + return(0); + + case '\\': + if ((c2 = *p++) == '\0') + return (0); + } + if (c1 != c2) + return (0); + } + return(unanch ? 1 : !*s); +} + +void usage(void) +{ + fprintf(stderr, "%s", message); + exit(1); +} + +int findmon(void) +{ + int i; + int month; + char *t; + static char tbuf[100]; + static const char *mon[12] = { + "[Jj][Aa][Nn][Uu. :1-9]", + "[Ff][Ee][Bb][Rr. :1-9]", + "[Mm][Aa][Rr][Cc. :1-9]", + "[Aa][Pp][Rr][Ii. :1-9]", + "[Mm][Aa][Yy][ :1-9]", + "[Jj][Uu][Nn][Ee. :1-9]", + "[Jj][Uu][Ll][Yy. :1-9]", + "[Aa][Uu][Gg][Uu. :1-9]", + "[Ss][Ee][Pp][Tt. :1-9]", + "[Oo][Cc][Tt][Oo. :1-9]", + "[Nn][Oo][Vv][Ee. :1-9]", + "[Dd][Ee][Cc][Ee. :1-9]" + }; + + t = &tbuf[0]; + for (i = 0; + ((*t = CurLinep[i]) != ':') && (*t != '\n') && (*t != '\0'); + i++) + ++t; + t[1] = '\0'; + t = &tbuf[0]; + + month = 0; + for (i = 0; i <= 11; i++) /* Look for month in word form. */ + if (pnmatch(t, mon[i], 1)) { + month = i+1; + break; + } + if (month == 0) { /* Month must be in numerical form */ + for (; *t && !isdigit(*t); t++) + CurLinep++; + while (*t && isdigit(*t)) { + month = 10*month + *t++ - '0'; + CurLinep++; + } + CurLinep++; /* Truncate global line pointer */ + } + if (month == 0 || month > 12) + month = -1; + return (month); +} + +int findday(void) +{ + register int day = 0; + + for (; *CurLinep && !isdigit(*CurLinep); CurLinep++); + ; + while (*CurLinep && isdigit(*CurLinep)) + day = 10*day + *CurLinep++ - '0'; + if (day == 0 || day > 31) /* Invalid day of the month */ + day = -1; + return (day); +} + +int findyear(void) +{ + int c; + int year; + + year = 0; + for (; (c = *CurLinep) != '\0' && !isdigit(c); CurLinep++) + if (c == '\n' || c == ':' || c == '\0') + break; + while (isdigit(*CurLinep)) + year = 10*year + *CurLinep++ -'0'; + if (year >= 83 && year <= 99) + year += 1900; + else if (year < 1990 || year > 3000) + year = -1; + return(year); +} + +extern int date(int day, int month, int year); + +int current(int opt) +{ + struct tm *stimep; + time_t timep; + int retval; + + time(&timep); + stimep = localtime(&timep); + switch (opt) { + case 0: + retval = date(stimep->tm_mday, stimep->tm_mon+1, stimep + ->tm_year+1900); + wday = stimep->tm_wday; + break; + case 1: + retval = stimep->tm_year+1900; + break; + case 2: + retval = stimep->tm_mon+1; + break; + default: + errx(1, "bad opt to current"); + } + return (retval); +} + +int date(int day, int month, int year) +{ + int date=0; + register int i; + static int m[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + if (year == 0) + year = current(1); + for (i = 0; i < month-1; i++) + date += m[i]; + date += day; + if ((year%4) == 0 && month > 2 && (year%100) != 0) + date++; + for (i = 1984; i < year; i++) { + if ((i%4) == 0) + date += 366; + else + date += 365; + } + return (date); +} + +/* FIXME: quoting and use getpwnam() */ +void doall(void) +{ + FILE *fp; + static char pline[256]; /* Current line in passwd file */ + static char uname[32]; + static char ucalfile[128]; + static char cmd[128]; + char *cp1, *cp2, *cp3; + int i; + + if ((fp = fopen("/etc/passwd", "r")) == NULL) + err(1, "cannot open /etc/passwd"); + while ((fgets(&pline[0],sizeof(pline),fp) != NULL) && *pline) { + for (cp1 = &pline[0]; *cp1 != '\0'; cp1++) + if (*cp1 == '\n') { + *cp1 = '\0'; + break; + } + cp1 = &pline[0]; + for (cp2 = &uname[0]; *cp1 != '\0' && *cp1 != ':'; ) + *cp2++ = *cp1++; + *cp2 = '\0'; + for (i = 0; i < 4 && *cp1 != '\0'; i++) + for (cp1++; *cp1 != '\0' && *cp1 != ':'; cp1++) + ; + cp1++; + cp3 = &ucalfile[0]; + for (; *cp1 != '\0' && *cp1 != ':'; ) + *cp3++ = *cp1++; + *cp3 = '\0'; + if (ucalfile[0] == '\0') + continue; + strcat(ucalfile, "/.calendar"); + if (open(ucalfile, 0) >= 0) { /* file is readable */ + strcpy(cmd, argv0); + strcat(cmd, " -f"); + strcat(cmd, ucalfile); + strcat(cmd, " | /bin/mail "); + strcat(cmd, uname); + system(cmd); + } + } + exit(0); +} + +int main(int argc, char *argv[]) +{ + int arg = 1; + char *cp; + char *thisline; + char *matchstr; + char *filename[10]; + FILE *fp[10]; + int matchdate; + int foundfiles; + int nfiles; + int thismonth, thisday, thisyear; + int thisdate; + char *atsign; + + argv0 = argv[0]; + mflag = NONE; /* Default to no match */ + for (arg = 1; arg < argc; arg++) { /* Read option string */ + cp = argv[arg]; + sw: switch (*cp) { + case '-': + cp++; + goto sw; + case 'a': + all = 1; + break; + case 'f': + filename[arg-1] = ++cp; + nfiles++; + continue; + case 'd': + matchstr = ++cp; + mflag = DAY; + continue; + case 'w': + matchstr = ++cp; + mflag = WEEK; + continue; + case 'm': + matchstr = ++cp; + mflag = MONTH; + continue; + default: + fprintf(stderr, "%s: unrecognized option '%c'\n", argv0, *cp); + usage(); + } + } + if (all) + doall(); + /* + * Open files. + */ + if (nfiles) { + for (arg = 0; arg < nfiles; arg++ ) { + if ((fp[arg] = fopen(filename[arg], "r")) == NULL) + fprintf(stderr, "cannot open file %s\n", filename[arg]); + else + foundfiles++; + } + if (!foundfiles) + err(1, "cannot open any files specified"); + } else { + char *hp; + + nfiles = 1; + if ((hp = getenv("HOME")) == NULL) + errx(1, "can't find my way back HOME"); + filename[0] = malloc(strlen(hp) + strlen(CALFILE) + 1); + if (filename[0] == NULL) + errx(1, "out of memory"); + strcpy(filename[0], hp); + strcat(filename[0], CALFILE); + if ((fp[0] = fopen(filename[0], "r")) == NULL) + err(1, "cannot open file $HOME/.calendar"); + } + /* + * Find match condition from options or current date + */ + switch (mflag) { + case NONE: + matchdate = current(0); + break; + case DAY: + case WEEK: + if (*matchstr == '\0') + matchdate = current(0); + else { + strncpy(CurLine, matchstr, sizeof(CurLine)); + CurLinep = &CurLine[0]; + if ((thismonth = findmon()) == -1) + errx(1, "invalid month in match date"); + if ((thisday = findday()) == -1) + errx(1, "invalid day in match date"); + if ((thisyear = findyear()) == -1) + thisyear = current(1); + matchdate = date(thisday, thismonth, thisyear); + } + break; + case MONTH: + if (*matchstr == '\0') + matchdate = current(2); + else { + strncpy(CurLine, matchstr, sizeof(CurLine)); + CurLinep = &CurLine[0]; + if ((matchdate = findmon()) == -1) + errx(1, "invalid month in match date"); + } + break; + } + /* + * Read the calendar files, print matched lines. + */ + for (arg = 0; arg < nfiles; arg++) { + if (fp[arg] == NULL) + continue; + while ((thisline = fgets(CurLine,sizeof(CurLine),fp[arg]))!=NULL) { + CurLinep = &CurLine[0]; + advance = 0; + if ((atsign = strchr(CurLinep, '@')) != NULL) + advance = atoi(atsign + 1); + if ((thismonth = findmon()) == -1) + thismonth = 0; + if ((thisday = findday()) == -1) + thisday = 0; + if ((thisyear = findyear()) == -1) + thisyear = current(1); + thisdate = date(thisday, thismonth, thisyear); + if (thisdate >= matchdate && + thisdate <= matchdate + advance) + printf("%s", thisline); + else switch (mflag) { + case NONE: + if (wday == 6) + if (thisdate == matchdate || + thisdate == matchdate + 1 || + thisdate == matchdate + 2 || + thisdate == matchdate + 3) + printf("%s", thisline); + if (wday == 7) + if (thisdate == matchdate || + thisdate == matchdate + 1 || + thisdate == matchdate + 2) + printf("%s", thisline); + if (0 <= wday && wday < 6) + if (thisdate == matchdate || + thisdate == matchdate + 1) + printf("%s", thisline); + break; + case DAY: + if (thisdate == matchdate) + printf("%s", thisline); + break; + case WEEK: + if (matchdate <= thisdate && + thisdate <= matchdate+7) + printf("%s", thisline); + break; + case MONTH: + if (thismonth == matchdate) + printf("%s", thisline); + break; + } + thisline = NULL; + } + } + return 0; +} + diff --git a/Applications/MWC/cmd/col.c b/Applications/MWC/cmd/col.c new file mode 100644 index 00000000..00da14ed --- /dev/null +++ b/Applications/MWC/cmd/col.c @@ -0,0 +1,599 @@ +/* + * Col(1). Virtual typewriter, performs motions physical typewriters cannot, + * like reverse line feeds. Also filters control characters. + * Two global variables, LineNo and ColNo, have the current line and column + * numbers. Both start at zero. + */ +#include +#include +#include +#include + +#define Unfetch(c) Unfetched = (c) + +#define PAGESIZE 256 /* Default Page size. */ +#define IBUFSIZE 800 /* Number of possible columns. */ +#define EBUFSIZ 4 /* Increment size for buf in EXTRA struct. */ + +/* + * Fetch returns the control characters \n, \b, \t, \r, HVT, VT, HLF, LF. + * We choose HVT and HLF to make a switch statement more compact in Main(). + */ +#define OSTRK 037 /* Flag for overstruck chars. */ +#define ESC 033 /* Escape character. */ +#define SO 017 /* Shift out to alternate char set. */ +#define SI 016 /* Shift in from alternate char set. */ +#define HVT 014 /* Half vertical tab. */ +#define VT 013 /* Vertical tab, or Rev. Line Feed. */ +#define HLF 007 /* Half line feed. */ +#define LF 006 /* Line feed (but not return!). */ +#define EOT 004 /* End of line signal to InsChar. */ +#define BOT 003 /* Start of line signal to InsChar. */ + +#define not ! +#define or || +#define and && +#define TRUE (0==0) +#define FALSE (not TRUE) +#define NOTREACHED return + +typedef uchar bool; + +typedef struct extra { + struct extra *Next; + int Posn; + uchar Howmany; + char Ebuf[EBUFSIZ]; +} EXTRA; + +typedef struct { + int Len; /* Number of valid columns in Line. */ + char *Line; + struct extra *Extra; +} LINE; + + +/* + * Error Messages. + */ +char Usage[] = "Usage: col [-bdfx] [-pnum]"; +char BackScroll[] = "Scrolling backwards over top of page window."; +char Confused[] = "Seem to have lost Overstruck characters."; + +/* + * Flags. + */ +bool Bflag = FALSE; /* Command line option 'b'. */ +bool Dflag = FALSE; /* Command line option 'd'. */ +bool Fflag = FALSE; /* Command line option 'f'. */ +bool Pflag = FALSE; /* Command line option 'p'. */ +bool Xflag = FALSE; /* Command line option 'x'. */ + +/* + * External Variables. + */ +char Ibuf[BUFSIZ]; /* Buffer for input lines. */ + +LINE *Page; /* Page window. */ +LINE *CurLine; /* Ptr to *(Page[LineNo % PageSize]). */ + +int PageSize = PAGESIZE; /* Actual size of Page. */ +int Ibuflen; /* Current length of line in Ibuf. */ +int Unfetched; /* storage for unfetched char */ +int Top; /* Line number of top of Page window. */ +int Bottom; /* Line number of bottom of Page window. */ +int Wmark; /* High-water-mark of Lineno. */ +int LineNo; /* Current line number in Page. */ +int ColNo; /* Current column number. */ + +void Warning(const char *cp) +{ + fputs(cp, stderr); + putc('\n', stderr); + return; +} + +void Fatal(const char *cp) +{ + Warning(cp); + exit(1); +} + +void OOM(void) +{ + Fatal("Out of memory"); +} + +/* + * Process command line arguments. + */ + +void Aarghh(int ac, char *av[]) +{ + register char *cp; + register int c; + + while ((cp = *++av) != NULL) { + if (*cp++ != '-') { + Fatal(Usage); + NOTREACHED; + } + while ((c = *cp++) != '\0') + switch (c) { + case 'b': + Bflag = TRUE; + break; + case 'd': + Dflag = TRUE; + break; + case 'f': + Fflag = TRUE; + break; + case 'x': + Xflag = TRUE; + break; + case 'p': + Pflag = TRUE; + if ((PageSize = 2 * atoi(cp)) <= 0) + Fatal("Bad page length"); + break; + default: + Fatal(Usage); + NOTREACHED; + } + } +} + +void Init(void) +{ + register LINE *lp; + + lp = CurLine = Page = (LINE *) malloc(sizeof(LINE) * PageSize); + if (lp == NULL) + OOM(); + lp += PageSize - 1; + while (lp-- > Page) { + lp->Len = 0; + lp->Line = NULL; + lp->Extra = NULL; + } + Bottom = PageSize; + return; +} + + +/* + * Fetch grabs input characters and returns them after filtering. + */ +int Fetch(void) +{ + static int acset = 0; /* alternate character set flag */ + register int c, c1; + + if ((c = Unfetched) != '\0') { + Unfetched = '\0'; + return (c); + } + for (;;) { + if ((c = getchar()) > ' ' && c < 0177) + return (acset ? c | 0200 : c); + switch (c) { + case ' ': + case '\t': + case '\n': + case '\b': + case '\r': + case VT: + case EOF: + return (c); + case SO: + acset = 1; + continue; + case SI: + acset = 0; + continue; + case ESC: + switch (c1 = getchar()) { + case '7': + return (VT); + case '8': + return (HVT); + case '9': + return (HLF); + case 'B': + return (LF); + } + ungetc(c1, stdin); + continue; + } + } +} + + + +/* + * Handle overstruck characters. + */ +void Overstrike(int c) +{ + register EXTRA *ep; + register EXTRA **epp; + + /* + * Find the right EXTRA struct in CurLine. + */ + epp = &CurLine->Extra; + for (ep = *epp; ep != NULL; epp = &ep->Next, ep = *epp) { + if (ep->Posn != ColNo) + continue; + /* + * We found it, check for overflow and add the char c. + */ + if (ep->Howmany % EBUFSIZ == 0) + ep = *epp = (EXTRA *) realloc((char *) ep, + sizeof(EXTRA) + + ep->Howmany); + if (ep == NULL) + OOM(); + ep->Ebuf[ep->Howmany++] = c; + return; + } + + /* + * We didn't find it, so make it, and install the char c. + */ + ep = (EXTRA *) malloc(sizeof(EXTRA)); + if (ep == NULL) + OOM(); + ep->Howmany = 1; + ep->Posn = ColNo; + ep->Ebuf[0] = c; + ep->Next = CurLine->Extra; + CurLine->Extra = ep; + return; +} + +/* + * Insert the character c into Ibuf at column ColNo. + */ + +void InsChar(int c) +{ + + /* + * If (c == BOT) or (c == EOT) we open or close the line in Ibuf. + * Otherwise we are really adding a character to Ibuf. + */ + if (c == BOT) { + register LINE *lp = CurLine; + if ((Ibuflen = lp->Len) != 0) { + strncpy(Ibuf, lp->Line, Ibuflen); + free(lp->Line); + } + return; + } else if (c == EOT) { + register LINE *lp = CurLine; + if ((lp->Len = Ibuflen) != 0) { + lp->Line = malloc(Ibuflen); + if (lp->Line == NULL) + OOM(); + strncpy(lp->Line, Ibuf, Ibuflen); + } + return; + } + + /* + * The case of appending a char to the end of Ibuf. Very common. + * Note that in this case Ibuf[ColNo] is virgin territory. + */ + if (ColNo == Ibuflen) { + Ibuf[Ibuflen++] = c; + return; + } + + /* + * The case of adding a char beyond the end of Ibuf. We must pad the + * intervening space with spaces. + */ + if (ColNo > Ibuflen) { + register char *ibuf = Ibuf; + while (Ibuflen < ColNo) + ibuf[Ibuflen++] = ' '; + ibuf[Ibuflen++] = c; + return; + } + + + /* + * The remaining case is adding a char into the interior of Ibuf. If + * the present char is a space or if Bflag is set we just insert c, + * otherwise we have to overstrike. + */ + { + register char *cp = Ibuf + ColNo; + register int c1; + + if (Bflag or(c1 = *cp) == ' ') { + *cp = c; + return; + } + if (c1 != OSTRK) { + Overstrike(c1); + *cp = OSTRK; + } + Overstrike(c); + } + return; +} + +/* + * Ostrikeout puts out all the characters overstruck in position ColNo in the + * LINE lp. It pays attention to alternate character sets. + */ +bool Ostrikeout(LINE * lp, int col, bool acset) +{ + EXTRA *ep; + + /* + * Find the EXTRA struct for position 'col' and remove it from + * the EXTRA list. + */ + { + register EXTRA **epp = &lp->Extra; + register EXTRA *e; + register int n = col; + + for (e = *epp; e != NULL; epp = &e->Next, e = *epp) { + if (e->Posn != n) + continue; + *epp = e->Next; + ep = e; + goto FOUNDIT; + } + /* + * Didn't find it in the list. + */ + putchar(' '); + Warning(Confused); + return (acset); + } + + /* + * Now that we found it, write out all the characters in ep->Ebuf, + * paying attention to alternate char sets, then free ep. + */ + FOUNDIT: + { + register int c; + register bool ac = acset; + register int count = ep->Howmany; + register char *cp = ep->Ebuf; + + while (count-- > 0) { + if ((c = *cp++) & 0200) { + if (not ac) { + ac = TRUE; + putchar(SO); + } + putchar(c & ~0200); + } else { + if (ac) { + ac = FALSE; + putchar(SI); + } + putchar(c); + } + if (count > 0) + putchar('\b'); + } + free(ep); + return (ac); + } +} + +/* + * PutHalf() outputs the half-line lp with no vertical motion at the end. + * Alternate character sets are handled here. Entabbing is handled by Tab. + * Overstrikes are handled by Ostrikeout(), which also may have to handle + * alternate character sets. + */ +void PutHalf(LINE * lp) +{ + register int c; + register int colno; + register bool acset = FALSE; + + /* + * Note that since lp->Len is the number of valid columns, the number + * of the last valid column is (lp->Len - 1). That's why the test is + * "<" instead of "<=". + */ + for (colno = 0; colno < lp->Len; ++colno) + switch (c = lp->Line[colno]) { + case OSTRK: + acset = Ostrikeout(lp, colno, acset); + break; + case ' ': + if (Xflag) { + putchar(' '); + break; + } + /* Entab white space. */ + c = colno; + while (lp->Line[c] == ' ') + if (++c % 8 == 0) { + putchar('\t'); + colno = c; + } + while (colno < c) { + putchar(' '); + ++colno; + } + --colno; + break; + default: + if (c & 0200) { + if (not acset) { + acset = TRUE; + putchar(SO); + } + putchar(c & ~0200); + } else { + if (acset) { + acset = FALSE; + putchar(SI); + } + putchar(c); + } + break; + } + if (acset) + putchar(SI); + free(lp->Line); + lp->Len = 0; + lp->Line = NULL; + lp->Extra = NULL; + return; +} + +/* + * Output one full line, which is possibly two half lines. All control for + * Dflag and Fflag takes place here. + */ +void PutLine(int n) +{ + static char HCR[] = { '\r', ESC, '9', '\0' }; /* Half Crg. Return */ + register LINE *lp; + + lp = Page + n % PageSize; + if (lp->Len != 0) + PutHalf(lp); + ++lp; + + if (lp->Len == 0) { + if (Dflag and not Fflag) { + putchar('\n'); + putchar('\n'); + } else + putchar('\n'); + } else { + if (Fflag) { + fputs(HCR, stdout); + PutHalf(lp); + fputs(HCR, stdout); + } else { + putchar('\n'); + PutHalf(lp); + putchar('\n'); + } + } + return; +} + +void VertMove(int c) +{ + static bool warnflag = FALSE; /* Warn of move over top of page. */ + + Unfetch(c); + for (;;) { + switch (c = Fetch()) { + case VT: + LineNo -= 2; + break; + case LF: + LineNo += 2; + break; + case HVT: + LineNo -= 1; + break; + case HLF: + LineNo += 1; + break; + case '\n': + ColNo = 0; + LineNo += 2; + break; + default: + Unfetch(c); + CurLine = Page + LineNo % PageSize; + return; + } + + /* + * Have we scrolled over the top of the Page window? + */ + if (LineNo < Top) + if (not warnflag) { + Warning(BackScroll); + warnflag = TRUE; + } + + /* + * Update the water mark if needed. + */ + if (LineNo > Wmark) + Wmark = LineNo; + + + /* + * If we've scrolled past the bottom of the window then we + * put out the Top line and move the window down. + */ + if (Bottom <= LineNo) { + PutLine(Top); + Top += 2; + Bottom += 2; + } + } +} + +void Flush(void) +{ + register int i; + + for (i = Top; i <= Wmark; i += 2) + PutLine(i); + return; +} + + + +int main(int ac, char *av[]) +{ + register int c; + + Aarghh(ac, av); + Init(); + InsChar(BOT); + while ((c = Fetch()) != EOF) + switch (c) { + case '\b': + if (ColNo > 0) + --ColNo; + break; + case '\r': + ColNo = 0; + break; + case '\t': + ColNo = (ColNo & ~07) + 8; + break; + case '\n': + ColNo = 0; + case VT: + case LF: + case HVT: + case HLF: + InsChar(EOT); /* Close current line. */ + VertMove(c); + InsChar(BOT); /* Open current line. */ + break; + default: + if (c != ' ') + InsChar(c); + ++ColNo; + break; + } + InsChar(EOT); /* Close current line. */ + Flush(); + return (0); +} diff --git a/Applications/MWC/cmd/deroff.c b/Applications/MWC/cmd/deroff.c new file mode 100644 index 00000000..6c2b8e82 --- /dev/null +++ b/Applications/MWC/cmd/deroff.c @@ -0,0 +1,507 @@ +/* + * Strip nroff/troff control lines and eqn + * and tbl sequences from input. + * Also, optionally, produce the + * output as a set of words. + */ + +#include +#include +#include +#include +#include + +#define NLINE 500 /* Input line length */ +#define NFNEST 15 /* Depth of .so file nesting */ + +FILE *ofiles[NFNEST]; +FILE **ofpp = &ofiles[0]; +char **flist; /* list of files to open */ + +typedef struct FNAME { + struct FNAME *fn_next; + char fn_name[0]; +} FNAME; + +FNAME *fnames; + +char line[NLINE]; + +int delim1 = EOF; /* Start embedded eqn */ +int delim2 = EOF; /* End embedd eqn */ +int skipcnt; /* Number of lines to skip */ +int skiptitle; /* Skip title text (until next nroff command) */ +int sflag; /* Divide into sentences */ +int wflag; /* Divide output into words */ +int xflag; /* Extra knowledge of macros (style/diction) */ + +int ineqn; /* Inside embedded eqn escape */ + + +/* + * Open input files (for .so, .nx, and from + * command line). Do not open any files twice. + */ +FILE *dopen(char *fname) +{ + register FNAME *fnp; + register FILE *fp; + + for (fnp = fnames; fnp != NULL; fnp = fnp->fn_next) + if (strcmp(fnp->fn_name, fname) == 0) + return (NULL); + if ((fp = fopen(fname, "r")) == NULL) + fprintf(stderr, "deroff: cannot open `%s'\n", fname); + else if ((fnp = + (FNAME *) malloc(sizeof(FNAME) + strlen(fname) + 2)) != + NULL) { + fnp->fn_next = fnames; + fnames = fnp; + strcpy(fnp->fn_name, fname); + } + return (fp); +} + + +/* + * Get a character from the next file stream. + */ +int dgetc(void) +{ + register int c; + + again: + if (*ofpp == NULL || (c = getc(*ofpp)) == EOF) { + if (*ofpp != stdin && *ofpp != NULL) + fclose(*ofpp); + if (ofpp > ofiles) { + ofpp--; + goto again; + } + while (*flist != NULL) + if ((*ofpp = dopen(*flist++)) != NULL) + goto again; + return (EOF); + } + return (c); +} + +/* + * Like fgets, only always reads using `dgetc' + * into a buffer of `NLINE' characters. + */ +char *dgets(char *as) +{ + register unsigned n = NLINE; + register char *s; + register int c; + + s = as; + while (--n > 0 && (c = dgetc()) != EOF) + if ((*s++ = c) == '\n') + break; + *s = '\0'; + return (c == EOF && s == as ? NULL : as); +} + +/* + * Output for that line which isn't an nroff + * control line. This has to look for embedded + * eqn stuff and back-slash troff/nroff escapes. + * The embedded escapes are all handled to some degree + * but such things as nested quotes (e.g. \w or \h) + * do not quite work. However, these occur almost never + * in text so it should be sufficient. + */ +void output(char *l) +{ + register int c; + register int inword = 0; + register int hyphen = 0; + + if (skipcnt) { + skipcnt--; + return; + } + if (skiptitle) + return; + while ((c = *l++) != '\0') { + if (ineqn) { + if (c == delim2) + ineqn = 0; + continue; + } + if (c == delim1) { + ineqn = 1; + continue; + } + if (c == '\\') { + if ((c = *l++) == '\0') + break; + switch (c) { + case '0': /* digit width space */ + case '|': /* Narrow space */ + case '&': /* Non-printing, 0-width char */ + case '!': /* Transparent line indicator */ + case '%': /* Optional hyphenation char */ + case 't': /* Non-interpreted tab */ + case 'u': /* Up 1/2 */ + case 'd': /* Down 1/2 */ + case 'a': /* Non-interpeted leader */ + case 'c': /* Interrupt text processing */ + case 'p': /* Break and spread */ + case 'r': /* Rerverse vertical motion */ + case '{': /* Begin conditional */ + case '}': /* End conditional */ + c = ' '; + break; + + case 'e': /* Current escape */ + c = '\\'; + break; + case '$': /* argument */ + if (*l != '\0') + l++; + case '^': /* Half narrow space */ + continue; + + case '(': /* Char named `xx' */ + if (*l != '\0') + l++; + if (*l != '\0') + l++; + c = ' '; + break; + + case 'z': /* Zero-width character */ + if (*l != '\0') + c = *l++; + break; + + case 'k': /* Mark input place in `x' */ + case 'n': /* Expand reggister x */ + case '*': /* Interpolate string */ + case 'f': /* Change font */ + if (*l != '\0') + if ((c = *l++) == '(') { + if (*l != '\0') + l++; + if (*l != '\0') + l++; + } + continue; + + case 's': /* Change point size */ + if (*l == '\0') + continue; + if ((c = *l++) == '-' || c == '+') { + if (*l == '\0') + continue; + c = *l++; + } + while (*l != '\0' && isdigit(c)) + c = *l++; + break; + + case 'x': /* Extra line space */ + case 'w': /* Width function */ + case 'v': /* Local vertical motion */ + case 'o': /* Overstrike function */ + case 'L': /* Vertical line */ + case 'l': /* Horizontal line */ + case 'h': /* Local horizontal motion */ + case 'b': /* Bracket-builder */ + if ((c = *l) != '\0') + while (*l != '\0' && *l != c) + l++; + continue; + + case '"': /* Beginning of comment */ + while (*l != '\0') + l++; + continue; + } + } + if (wflag) { + if (c == '\n') + continue; + if (!inword) + if (isalpha(c)) + inword = 1; + else + continue; + if (c == '-' && !hyphen) { + hyphen = 1; + continue; + } + hyphen = 0; + if (c == '\'') + continue; + if (!isalpha(c) && !isdigit(c)) { + inword = 0; + putchar('\n'); + continue; + } + } + putchar(c); + } + if (wflag && inword && !hyphen) + putchar('\n'); +} + + +/* + * Skip centred lines, in extended mode, + * by setting a skip counter on text. + */ +void centre(char *l) +{ + if ((skipcnt = atoi(l)) == 0) + skipcnt = 1; +} + +/* + * Skip displays in extended mode. + */ +void display(void) +{ + if (!xflag) + return; + while (dgets(line) != NULL) { + if (strncmp(line, ".KE", 3) == 0) + break; + if (strncmp(line, ".DE", 3) == 0) + break; + } +} + + +/* + * Process eqn directives. + * Currently, this simply looks for + * .EN lines as the terminator + * and delim lines to set the eqn delimiters. + */ +void eqn(void) +{ + register char *cp; + + while (dgets(line) != NULL) { + if (strncmp(line, ".EN", 3) == 0) + break; + if (strncmp(line, "delim", 5) == 0) { + for (cp = line + 5; *cp == ' ' || *cp == '\t'; + cp++); + if (*cp == '\n' || *cp == '\0') + continue; + if (strncmp(cp, "off", 3) == 0) { + delim1 = EOF; + delim2 = EOF; + continue; + } + delim1 = *cp++; + delim2 = *cp; + } + } +} + +/* + * In extended knowledge mode (-ms macros), + * remove footnotes. This mode is for + * style and diction. + */ +void footnote(void) +{ + if (!xflag) + return; + while (dgets(line) != NULL) + if (strncmp(line, ".FE", 3) == 0) + break; +} + +/* + * Include a file as per the `.so' request + * line. + */ +void dotso(char *fname) +{ + if (++ofpp > &ofiles[NFNEST - 1]) { + fprintf(stderr, "deroff: .so nested too deep--%s\n", + fname); + ofpp--; + return; + } + if ((*ofpp = dopen(fname)) == NULL) + ofpp--; +} + +/* + * Process included files. + * The first argument is the pointer to where + * the filename is (it may have junk before and after it) + * The second is 's' for .so and 'n' for .nx. + */ +void include(char *fn, char type) +{ + register int c; + register char *ep; + + while (*fn == ' ' || *fn == '\t') + fn++; + for (ep = fn; (c = *ep) != '\0'; ep++) + if (c == '\n' || c == ' ' || c == '\t' || c == '\\') + break; + *ep = '\0'; + if (type == 's') + dotso(fn); + else + *ofpp = dopen(fn); +} + +/* + * Remove a macro defintion. + */ +void macdef(void) +{ + while (dgets(line) != NULL) + if (strcmp(line, "..\n") == 0) + break; +} + +/* + * Throw away nofilled text as with footnotes above. + */ +void nofill(void) +{ + if (!xflag) + return; + while (dgets(line) != NULL) + if (strncmp(line, ".fi", 3) == 0) + break; +} + + + +/* + * Process tbl directives. At this time, + * all this does is look for the terminating + * .TE to end tables. + */ +void tbl(void) +{ + while (dgets(line) != NULL) + if (strncmp(line, ".TE", 3) == 0) + break; +} + +/* + * If in extended mode, skip titles and author's + * names. Set a flag to skip until next nroff command. + */ +void titles(void) +{ + if (xflag) + skiptitle = 1; +} + +/* + * Process nroff control lines. + * Remove EQN, TBL, macro defintions. + * Process .so and .nx here. + * Other lines have the rest of the line used. + */ +void nroff(char *l) +{ + skiptitle = 0; + if (l[1] == 'E' && l[2] == 'Q') + eqn(); + else if (l[1] == 'T' && l[2] == 'S') + tbl(); + else if (l[1] == 'F' && l[2] == 'S') + footnote(); + else if (l[1] == 'c' && l[2] == 'e') + centre(l); + else if (l[1] == 'n' && l[2] == 'f') + nofill(); + else if (l[1] == 'D' && l[2] == 'S') + display(); + else if (l[1] == 'K' && (l[2] == 'F' || l[2] == 'S')) + display(); + else if (l[1] == 'T' && l[2] == 'L') + titles(); + else if (l[1] == 'A' && (l[2] == 'I' || l[2] == 'U')) + titles(); + else if (l[2] == 'H' && (l[1] == 'S' || l[1] == 'N')) + titles(); + else if (l[1] == 'n' && l[2] == 'x') + include(line + 3, 'n'); + else if (l[1] == 's' && l[2] == 'o') + include(line + 3, 's'); + else if (l[1] == 'd' && l[2] == 'e') + macdef(); + else if (l[1] == 'd' && l[2] == 's') + return; + else { + while (*l != ' ' && *l != '\t' && *l != '\0') + l++; + while (*l == ' ' || *l == '\t') + l++; + if (*l != '\0') + output(l); + } +} + +/* + * Read until end-of-file + * and process the special nroff/troff/eqn/tbl + * lines in the file. + */ +void deroff(void) +{ + + while (dgets(line) != NULL) { + if (!ineqn && line[0] == '.') { + nroff(line); + continue; + } + output(line); + } +} + +void usage(void) +{ + fprintf(stderr, "Usage: deroff [ -w ] [ -x ] [file ...]\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + register char *ap; + + while (argc > 1 && *argv[1] == '-') { + for (ap = &argv[1][1]; *ap != '\0'; ap++) + switch (*ap) { + case 's': + sflag = 1; + break; + + case 'w': + wflag = 1; + break; + + case 'x': + xflag = 1; + break; + + default: + usage(); + } + argv++; + argc--; + } + if (argc < 2) + ofiles[0] = stdin; + flist = &argv[1]; + deroff(); + exit(0); +} diff --git a/Applications/MWC/cmd/m4.c b/Applications/MWC/cmd/m4.c new file mode 100644 index 00000000..248ad851 --- /dev/null +++ b/Applications/MWC/cmd/m4.c @@ -0,0 +1,1020 @@ +#include +#include +#include +#include +#include + +#define TMPFILE "/tmp/m4%dxxxxxx" +#define TMPMIN 6 + +#define isletter(c) (isalpha(c)||c=='_') +#define BQUOTE '`' +#define EQUOTE '\'' +#define HASHSZ 199 +#define STRBLK 16 /* block size for dynamic string storage */ +#if STRBLK < 12 +need room for decimal rep of long int +#endif + enum { FUNC, MACD, DSKF, MSTR }; +enum { EX, MY, DV, MD, AD, SB, EQ, NE, LE, GE, LT, GT, AN, OR, RP, NL }; + +typedef struct { /* for storing variable-length strings */ + int s_refc; /* references--when zero space is freed */ + int s_hash; /* raw hash value (sum of chars in body) */ + char *s_body; /* start of contents */ + char *s_next; /* end of contents */ + char *s_last; /* end of storage */ +} STRING; + +typedef union ifr { /* input-source stack frame */ + struct { + union ifr *i_back; + char i_cbuf; /* unget buffer for lookahead */ + int i_type; /* if type is MSTRing */ + STRING *i_pstr; + char *i_pchr; /* next char */ + } ifb; + struct { + union ifr *i_back; + char i_cbuf; + int i_type; /* if type is DSKFile */ + FILE *i_fp; + } ifdk; +} IFRAME; + +typedef struct fstk { /* file information stack frame */ + struct fstk *f_back; + STRING *f_name; /* pointer to name (NULL if stdin) */ + int f_flag; /* flag = 0 until EOF is reached */ + int f_line; /* line counter */ +} FFRAME; + +typedef struct ofr { /* argument collection stack frame */ + struct ofr *o_back; + STRING *o_pstr; +} OFRAME; + +typedef struct ent { /* symbol table entry */ + struct ent *e_next; + int e_type; /* FUNC or MACD */ + union { + void (*e_pfun) (STRING ** s); + STRING *e_pstr; + } e_at; + STRING *e_name; +} ENTRY; + +/* + * output file info + */ +struct { + char *name; + FILE *fp; +} outfile[10] = { { +NULL, stdout}}; + +/* + * op table for eval + */ +struct opdata { + int optype; + char keychar; + char secchar; + int prec; +} optable[] = { +#define SI 8 /* precedence of unary + and - */ + { + EX, '^', '\0', 7}, { + EX, '*', '*', 7}, { + MY, '*', '\0', 6}, { + DV, '/', '\0', 6}, { + MD, '%', '\0', 6}, { + AD, '+', '\0', 5}, { + SB, '-', '\0', 5}, { + EQ, '=', '=', 4}, { + GE, '>', '=', 4}, { + GT, '>', '\0', 4}, { + NE, '!', '=', 4}, { + LE, '<', '=', 4}, { + LT, '<', '\0', 4}, +#define NT 3 /* precedence of unary ! */ + { + AN, '&', '&', 2}, { + AN, '&', '\0', 2}, { + OR, '|', '|', 1}, { + OR, '|', '\0', 1}, { + RP, ')', '\0', 10}, { + NL, '\0', '\0', 0}, +#define LP 0 /* precedence of open paren */ +}; + +ENTRY *e_root[HASHSZ]; /* pointers to symbol table hash buckets */ +OFRAME *ostkptr; /* output stack pointer */ +IFRAME *istkptr; /* input stack pointer */ +FFRAME *fstkptr; /* file info stack pointer */ +FILE *offp = stdout; /* current output file pointer */ +int ofnum; /* current diversion number */ +int lstdchr = '\n'; /* last char from stdin */ +int single; /* single argument flag */ +int dnlflag; /* delete to newline flag */ + +char bqt = BQUOTE; +char eqt = EQUOTE; + +/* VARARGS */ +void errorp(int f, ...) +{ + fprintf(stderr, "m4: "); +#if 0 + FIXME if (fstkptr != NULL) { + if (fstkptr->f_name != NULL) + fprintf(stderr, "%s: ", fstkptr->f_name->s_body); + fprintf(stderr, "%d: ", fstkptr->f_line); + } + fprintf(stderr, "%r", &x); + putc('\n', stderr); + if (f) { + while (lstdchr != EOF && lstdchr != '\n') + lstdchr = getchar(); + exit(1); + } +#endif +} + +char *alloc(int n) +{ + char *x; + + if ((x = malloc(n)) != NULL) + return (x); + errorp(1, "out of space"); +} + +STRING *makestr(void) +{ + register STRING *a; + + a = (STRING *) alloc(sizeof(STRING)); + a->s_body = a->s_next = alloc(STRBLK); + a->s_last = a->s_body + STRBLK - 1; + a->s_refc = 1; + a->s_hash = 0; + *a->s_next = '\0'; + return (a); +} + +void decstr(STRING * a) +{ + if (a == NULL) + return; + if (--a->s_refc) + return; + free(a->s_body); + free(a); +} + +int cmpstr(STRING * a, STRING * b) +{ + if (a == NULL && b == NULL) + return (1); + if ((a == NULL && b != NULL) || (a != NULL && b == NULL)) + return (0); + return (!strcmp(a->s_body, b->s_body)); +} + +void appendstr(STRING * a, char c) +{ + register char *r, *s, *t; + int size; + + if (a->s_next < a->s_last) { + *a->s_next++ = c; + *a->s_next = '\0'; + } else { + r = s = alloc(size = + a->s_last - (t = a->s_body) + 1 + STRBLK); + while ((*s++ = *t++) != 0); + *(a->s_next = s) = '\0'; + *--s = c; + free(a->s_body); + a->s_last = (a->s_body = r) + size - 1; + } + a->s_hash += c; +} + +void pushinp(char t) +{ + IFRAME *itemp; + + itemp = istkptr; + istkptr = (IFRAME *) alloc(sizeof(IFRAME)); + istkptr->ifb.i_back = itemp; + istkptr->ifb.i_type = t; + istkptr->ifb.i_cbuf = '\0'; +} + +int pushfile(char *s) +{ + FILE *fp; + register STRING *a = NULL; + register FFRAME *ftemp = fstkptr; + char c; + + if (ftemp != NULL && ftemp->f_flag) { + decstr(ftemp->f_name); + fstkptr = ftemp->f_back; + free(ftemp); + } + if (strcmp(s, "-") == 0) + fp = stdin; + else if ((fp = fopen(s, "r")) == NULL) + return (0); + pushinp(DSKF); + istkptr->ifdk.i_fp = fp; + ftemp = (FFRAME *) alloc(sizeof(FFRAME)); + ftemp->f_back = fstkptr; + ftemp->f_line = 1; + ftemp->f_flag = 0; + if (fp != stdin && !(fstkptr == NULL && single)) { + a = makestr(); + while ((c = *s++) != 0) + appendstr(a, c); + } + ftemp->f_name = a; + fstkptr = ftemp; + return (1); +} + +void pushstr(STRING * a) +{ + if (a == NULL || *a->s_body == '\0') + return; + pushinp(MSTR); + istkptr->ifb.i_pstr = a; + istkptr->ifb.i_pchr = a->s_body; + ++a->s_refc; +} + +void pushnum(long x) +{ + register STRING *a; + + a = makestr(); + sprintf(a->s_body, "%ld", x); + pushstr(a); + decstr(a); +} + +int popinp(void) +{ + IFRAME *itemp; + + itemp = istkptr; + switch (istkptr->ifb.i_type) { + case MSTR: + decstr(istkptr->ifb.i_pstr); + break; + case DSKF: + if (istkptr->ifdk.i_fp != stdin) + fclose(istkptr->ifdk.i_fp); + fstkptr->f_flag = 1; + break; + } + istkptr = istkptr->ifb.i_back; + free(itemp); + return (istkptr != NULL); +} + +void pushout(STRING * b) +{ + OFRAME *otemp; + + otemp = (OFRAME *) alloc(sizeof(OFRAME)); + otemp->o_back = ostkptr; + otemp->o_pstr = b; + ostkptr = otemp; + ++b->s_refc; +} + +void popout(void) +{ + OFRAME *otemp; + + otemp = ostkptr; + ostkptr = ostkptr->o_back; + decstr(otemp->o_pstr); + free(otemp); +} + +void macro(STRING * ps, STRING ** pps) +{ + STRING *x; + register char *b, *v; + + x = makestr(); + b = ps->s_body; + while (*b) { + if (*b != '$') + appendstr(x, *b++); + else if (!isdigit(*++b)) + appendstr(x, '$'); + else if ((ps = pps[*b++ - '0']) != 0) + for (v = ps->s_body; *v; v++) + appendstr(x, *v); + } + pushstr(x); + decstr(x); +} + + +void outc(char c) +{ + if (offp != NULL) + putc(c, offp); +} + +void outputc(char c) +{ + if (ostkptr != NULL) + appendstr(ostkptr->o_pstr, c); + else + outc(c); +} + +void outputs(char *s) +{ + register char *t, c; + + for (t = s; (c = *t++) != 0; outputc(c)); +} + +int nxch(void) +{ + register int c = 0; + FFRAME *ftemp; + + if (istkptr == NULL) + return ('\0'); + if ((ftemp = fstkptr)->f_flag) { + decstr(ftemp->f_name); + fstkptr = ftemp->f_back; + free(ftemp); + } + switch (istkptr->ifb.i_type) { + case DSKF: + if ((c = istkptr->ifdk.i_cbuf) != 0) + istkptr->ifdk.i_cbuf = '\0'; + else + c = getc(istkptr->ifdk.i_fp); + if (istkptr->ifdk.i_fp == stdin) + lstdchr = c; + if (c == EOF) + c = '\0'; + else if (c == '\n') { + if (fstkptr->f_flag) + ++fstkptr->f_back->f_line; + else + ++fstkptr->f_line; + } + break; + case MSTR: + if ((c = istkptr->ifb.i_cbuf) != 0) + istkptr->ifb.i_cbuf = '\0'; + else + c = *istkptr->ifb.i_pchr++; + break; + } + if (c == '\0') + return (popinp()? nxch() : c); + else if (!dnlflag) + return (c); + if (c == '\n') + dnlflag = 0; + return (nxch()); +} + +ENTRY *find(STRING * a) +{ + register ENTRY *e; + register int hash; + + hash = a->s_hash; + for (e = e_root[hash % HASHSZ]; e != NULL; e = e->e_next) + if (e->e_name->s_hash == hash + && strcmp(e->e_name->s_body, a->s_body) == 0) + return (e); + return (NULL); +} + +int process(int pct) +{ + char lc = '\0'; + register int c, qct = 0; + int i; + STRING *a[10]; + register STRING *b; + ENTRY *e; + + for (c = nxch(); pct && isspace(c); c = nxch()); + while (c != '\0') { + if (qct) + if (c == eqt) { + if (--qct) + outputc(c); + } else { + if (c == bqt) + ++qct; + outputc(c); + } else if (c == bqt) + ++qct; + else if ((c == ')' && pct && !--pct) + || (c == ',' && pct == 1)) + return (c); + else if (isletter(c) && !isletter(lc) && !isdigit(lc)) { + b = makestr(); + do { + appendstr(b, c); + lc = c; + c = nxch(); + } while (isletter(c) || isdigit(c)); + if ((e = find(b)) == NULL) { + outputs(b->s_body); + decstr(b); + continue; + } + for (i = 9; i; a[i--] = NULL); + a[0] = b; + if (c != '(') { + istkptr->ifb.i_cbuf = c; + if (c == '\n' + && istkptr->ifb.i_type == DSKF) { + if (fstkptr->f_flag) + --fstkptr->f_back->f_line; + else + --fstkptr->f_line; + } + } else + do { + pushout(b = makestr()); + c = process(1); + if (i++ < 9 && strlen(b->s_body)) + a[i] = b; + else + decstr(b); + popout(); + } while (c != ')'); + if (e->e_type == MACD) + macro(e->e_at.e_pstr, a); + else + (*e->e_at.e_pfun) (a); + for (i = 0; i < 10; i++) + decstr(a[i]); + c = '\0'; + } else { + if (c == '(' && pct) + ++pct; + outputc(c); + } + lc = c; + c = nxch(); + } + if (pct) + errorp(1, "unexpected EOF"); + return (c); +} + +void buildin(char *s, void (*f) (STRING ** p)) +{ + STRING *a; + register int hash; + register ENTRY *e; + + a = makestr(); + while (*s) + appendstr(a, *s++); + hash = a->s_hash % HASHSZ; + e = (ENTRY *) alloc(sizeof(ENTRY)); + e->e_next = e_root[hash]; + e->e_type = FUNC; + e->e_at.e_pfun = f; + e->e_name = a; + e_root[hash] = e; +} + +void mchangequote(STRING ** pps) +{ + bqt = pps[1] ? *pps[1]->s_body : BQUOTE; + eqt = pps[2] ? *pps[2]->s_body : EQUOTE; +} + +void mdefine(STRING ** pps) +{ + register ENTRY *e; + register char *s; + register int hash; + int c; + static char illmac[] = "illegal macro name: %s"; + + if (pps[1] == NULL || !isletter(*pps[1]->s_body)) { + errorp(0, illmac, pps[1] != NULL ? pps[1]->s_body : NULL); + return; + } + s = pps[1]->s_body + 1; + while ((c = *s++) != 0) + if (!(isletter(c) || isdigit(c))) { + errorp(0, illmac, pps[1]->s_body); + return; + } + if ((e = find(pps[1])) != NULL) { + if (e->e_type == MACD) + decstr(e->e_at.e_pstr); + } else { + e = (ENTRY *) alloc(sizeof(ENTRY)); + e->e_next = e_root[hash = pps[1]->s_hash % HASHSZ]; + e->e_name = pps[1]; + ++pps[1]->s_refc; + e_root[hash] = e; + } + e->e_type = MACD; + if (pps[2]) { + e->e_at.e_pstr = pps[2]; + ++pps[2]->s_refc; + } else + e->e_at.e_pstr = makestr(); +} + +void mdivert(STRING ** pps) +{ + char *fn; + int fd; + + if (pps == NULL) + ofnum = 0; + else + ofnum = (pps[1] != NULL) ? atoi(pps[1]->s_body) : 0; + if (ofnum > 0 && ofnum <= 9) { + if (outfile[ofnum].fp == NULL) { + outfile[ofnum].name = fn = alloc(15); + sprintf(fn, TMPFILE, ofnum); + fd = mkstemp(fn); + if (fd == -1 + || (outfile[ofnum].fp = + fdopen(fd, "w")) == NULL) + errorp(1, "m4: /tmp open error\n"); + } + } + if (ofnum >= 0 && ofnum <= 9) + offp = outfile[ofnum].fp; + else + offp = NULL; +} + +void mdivnum(STRING ** unused) +{ + pushnum((long) ofnum); +} + +void mdnl(STRING ** unused) +{ + dnlflag = 1; +} + +void outdef(ENTRY * e) +{ + if (e->e_type == MACD) { + outputc(bqt); + outputs(e->e_at.e_pstr->s_body); + outputc(eqt); + } +} + +void mdumpdef(STRING ** pps) +{ + register ENTRY *e; + register int i, f = 0; + int hash; + + for (i = 1; i <= 9; ++i) + if (pps[i] != NULL) { + if ((e = find(pps[i])) != NULL) + outdef(e); + f = 1; + } + if (!f) + for (hash = 0; hash < HASHSZ; ++hash) + for (e = e_root[hash]; e; e = e->e_next) { + outputc(bqt); + outputs(e->e_name->s_body); + outputc(eqt); + outputc('\t'); + outdef(e); + outputc('\n'); + } +} + +void merrprint(STRING ** pps) +{ + register int i; + + for (i = 1; i <= 9; i++) + if (pps[i]) + fprintf(stderr, pps[i]->s_body); +} + +long calc(int pr, char **ps) +{ + register struct opdata *opptr; + register char c; + char *s; + long val1, val2, l; + int oplength; + + for (s = *ps; isspace(*s); ++s); + if (isdigit(c = *s++)) + for (val1 = c - '0'; isdigit(c = *s); ++s) + val1 = 10 * val1 + c - '0'; + else + switch (c) { + case '+': + val1 = calc(SI, &s); + break; + case '-': + val1 = -calc(SI, &s); + break; + case '!': + val1 = !calc(NT, &s); + break; + case '(': + val1 = calc(LP, &s); + break; + default: + if (c == '\0') + errorp(0, "eval: missing value"); + else + errorp(0, "eval: invalid expression"); + s = NULL; + } + if ((*ps = s) == NULL) + return (0); + for (;;) { + while (s != NULL && isspace(c = *s)) + ++s; + oplength = 1; + for (opptr = optable; s != NULL; ++opptr) + if (c != opptr->keychar) { + if (opptr->keychar == '\0') + s = NULL; + } else if (opptr->secchar == '\0') { + break; + } else if (opptr->secchar == *(s + 1)) { + ++oplength; + break; + } + if ((*ps = s) == NULL) { + errorp(0, "eval: missing or unknown operator"); + return (0); + } + if (opptr->prec <= pr) + return (val1); + *ps = s += oplength; + if (c == ')') + return (val1); + val2 = calc(opptr->prec, &s); + if ((*ps = s) == NULL) + return (0); + switch (opptr->optype) { + case EX: + if (val2 < 0) + val1 = 0; + else { + for (l = 1; val2; --val2) + l *= val1; + val1 = l; + } + break; + case MY: + val1 *= val2; + break; + case DV: + val1 /= val2; + break; + case MD: + val1 %= val2; + break; + case AD: + val1 += val2; + break; + case SB: + val1 -= val2; + break; + case EQ: + val1 = (val1 == val2); + break; + case NE: + val1 = (val1 != val2); + break; + case GE: + val1 = (val1 >= val2); + break; + case LE: + val1 = (val1 <= val2); + break; + case GT: + val1 = (val1 > val2); + break; + case LT: + val1 = (val1 < val2); + break; + case AN: + val1 = (val1 && val2); + break; + case OR: + val1 = (val1 || val2); + break; + } + } +} + +void meval(STRING ** pps) +{ + char *s; + + if (pps[1] == NULL) + pushnum((long) 0); + else { + s = pps[1]->s_body; + pushnum(calc(0, &s)); + } +} + +void mifdef(STRING ** pps) +{ + if (pps[1] && find(pps[1])) { + pushstr(pps[2]); + } else + pushstr(pps[3]); +} + +int moreargs(STRING ** pps, int n) +{ + register int i; + + for (i = n; i <= 9; i++) + if (pps[i] != NULL) + return (1); + return (0); +} + +void mifelse(STRING ** pps) +{ + if (cmpstr(pps[1], pps[2])) + pushstr(pps[3]); + else if (moreargs(pps, 5)) + if (cmpstr(pps[4], pps[5])) + pushstr(pps[6]); + else if (moreargs(pps, 8)) + if (cmpstr(pps[7], pps[8])) + pushstr(pps[9]); + else + return; + else + pushstr(pps[7]); + else + pushstr(pps[4]); +} + +int doinclude(STRING ** pps) +{ + return (pushfile((pps[1] != NULL) ? pps[1]->s_body : NULL)); +} + +void msinclude(STRING ** pps) +{ + doinclude(pps); +} + +void minclude(STRING ** pps) +{ + if (doinclude(pps)) + return; + errorp(1, "cannot open %s", + pps[1] != NULL ? pps[1]->s_body : NULL); +} + +void mincr(STRING ** pps) +{ + pushnum((long) ((pps[1] != NULL) ? atol(pps[1]->s_body) + 1 : 1)); +} + +void mdecr(STRING ** pps) +{ + pushnum((long) ((pps[1] != NULL) ? atol(pps[1]->s_body) - 1 : -1)); +} + +void mindex(STRING ** pps) +{ + register char *pc, *pf; + register int ln; + long v; + + if (pps[2] == NULL) + v = 0; + else if (pps[1] == NULL) + v = -1; + else { + pc = pps[1]->s_body; + ln = strlen(pf = pps[2]->s_body); + while ((pc = index(pc, *pf)) != NULL + && strncmp(pc, pf, ln)) + ++pc; + v = (pc != NULL) ? (long) (pc - pps[1]->s_body) : -1; + } + pushnum(v); +} + +void mlen(STRING ** pps) +{ + pushnum((long) + ((pps[1] != NULL) ? pps[1]->s_next - pps[1]->s_body : 0)); +} + +void mmaketemp(STRING ** pps) +{ + register char *pc; + if (pps[1] == NULL || strlen(pc = pps[1]->s_body) < TMPMIN) + return; + mktemp(pc); + pushstr(pps[1]); +} + +void msubstr(STRING ** pps) +{ + register char *pc, *pb, *pe; + int len, n, s; + STRING *a; + + if (pps[1] == NULL) + return; + len = strlen(pc = pps[1]->s_body); + if (pps[2] == NULL) { + n = (pps[3] != NULL) ? atoi(pps[3]->s_body) : len; + s = (n > 0) ? 0 : -1; + } else { + s = atoi(pps[2]->s_body); + n = (pps[3] != NULL) ? atoi(pps[3]->s_body) : (s > + 0) ? len : + -len; + } + if (n == 0) + return; + pe = pc + len; + if (s < 0) + s += len; + if (n < 0) { + s += n + 1; + n = -n; + } + a = makestr(); + for (pb = pc + s; n--; ++pb) + if (pb >= pc && pb <= pe) + appendstr(a, *pb); + pushstr(a); + decstr(a); +} + +void msyscmd(STRING ** pps) +{ + if (pps[1]) + system(pps[1]->s_body); +} + +void mtranslit(STRING ** pps) +{ + register char *pc, *pt, *pr; + char c = '\0'; + STRING *a; + + if (pps[1] == NULL || pps[2] == NULL) + return; + a = makestr(); + pc = pps[1]->s_body; + do { + pt = pps[2]->s_body; + pr = (pps[3] != NULL) ? pps[3]->s_body : &c; + do { + if (*pc == *pt) + break; + + if (*pr) + ++pr; + } while (*++pt); + if (*pt) { + if (*pr) + appendstr(a, *pr); + } else + appendstr(a, *pc); + } while (*++pc); + pushstr(a); + decstr(a); +} + +void mundefine(STRING ** pps) +{ + register ENTRY *e, *ep = NULL; + register int hash; + + if (pps[1] == NULL) + return; + hash = pps[1]->s_hash; + for (e = e_root[hash % HASHSZ]; e != NULL; e = e->e_next) + if (e->e_name->s_hash == hash + && strcmp(e->e_name->s_body, pps[1]->s_body) == 0) + break; + else + ep = e; + if (e == NULL) + return; + if (e->e_type == MACD) + decstr(e->e_at.e_pstr); + decstr(e->e_name); + if (ep != NULL) + ep->e_next = e->e_next; + else + e_root[hash % HASHSZ] = e->e_next; + free(e); +} + +void undiv(int n) +{ + register int c; + register FILE *fp; + + if (n > 0 && n <= 9 && (fp = outfile[n].fp) != NULL && ofnum != n) { + fclose(fp); + if ((fp = fopen(outfile[n].name, "r")) == NULL) + errorp(1, "cannot open %s", outfile[n].name); + while ((c = getc(fp)) != EOF) + outc(c); + fclose(fp); + unlink(outfile[n].name); + free(outfile[n].name); + outfile[n].name = NULL; + outfile[n].fp = NULL; + } +} + +void mundivert(STRING ** pps) +{ + register int i, f = 0; + + if (pps) + for (i = 1; i <= 9; i++) + if (pps[i]) { + undiv(atoi(pps[i]->s_body)); + f = 1; + } + if (pps == NULL || f == 0) + for (i = 1; i <= 9; i++) + undiv(i); +} + +int main(int argc, char *argv[]) +{ + buildin("changequote", mchangequote); + buildin("decr", mdecr); + buildin("define", mdefine); + buildin("divert", mdivert); + buildin("divnum", mdivnum); + buildin("dnl", mdnl); + buildin("dumpdef", mdumpdef); + buildin("errprint", merrprint); + buildin("eval", meval); + buildin("ifdef", mifdef); + buildin("ifelse", mifelse); + buildin("include", minclude); + buildin("incr", mincr); + buildin("index", mindex); + buildin("len", mlen); + buildin("maketemp", mmaketemp); + buildin("sinclude", msinclude); + buildin("substr", msubstr); + buildin("syscmd", msyscmd); + buildin("translit", mtranslit); + buildin("undefine", mundefine); + buildin("undivert", mundivert); + + single = (argc == 2); + if (argc > 1) { + while (--argc) + if (pushfile(*++argv)) + process(0); + else + errorp(0, "cannot open %s", *argv); + } else { + pushfile("-"); + process(0); + } + mdivert(NULL); + mundivert(NULL); + exit(0); +} diff --git a/Applications/MWC/cmd/moo.c b/Applications/MWC/cmd/moo.c new file mode 100644 index 00000000..1aba3501 --- /dev/null +++ b/Applications/MWC/cmd/moo.c @@ -0,0 +1,87 @@ +/* + * moo -- play the game of moo, + * also known as mastermind. + */ + +#include +#include +#include +#include + + +char entdiff[] = "Please enter a string of %d digits, all different\n"; + +int badline(char *s, int l) +{ + int i, j; + if (strlen(s) != l + 1) + return (1); + s[l] = 0; + for (i = 0; i != l; i++) { + if (s[i] < '0' || '9' < s[i]) + return (1); + for (j = 0; j != i; j++) + if (s[i] == s[j]) + return (1); + } + return (0); +} + +int main(int argc, char *argv[]) +{ + char moo[10]; + int nmoo; + char line[256]; + int i, j; + int nbull, ncow; + union { + time_t utime; + int ubuf[2]; + } un; + + time(&un.utime); + srand(un.ubuf[0] + un.ubuf[1]); + if (argc < 2) + nmoo = 4; + else + nmoo = atoi(argv[1]); + if (nmoo < 1 || 10 < nmoo) { + printf + ("Usage: moo [n]\n\twhere 1<=n<=10, n defaults to 4\n"); + exit(1); + } + for (;;) { + printf("New game.\n"); + for (i = 0; i != nmoo; i++) { + Again: + moo[i] = rand() / 100 % 10 + '0'; + for (j = 0; j != i; j++) + if (moo[i] == moo[j]) + goto Again; + } + for (;;) { + if (fgets(line, 255, stdin) == NULL) + exit(0); + if (badline(line, nmoo)) + printf(entdiff, nmoo); + else { + nbull = 0; + ncow = 0; + for (i = 0; i != nmoo; i++) + for (j = 0; j != nmoo; j++) + if (line[i] == moo[j]) + if (i == j) + nbull++; + else + ncow++; + if (nbull == nmoo) { + printf("Right!\n"); + break; + } + printf("%d bull%c, %d cow%c\n", + nbull, nbull != 1 ? 's' : '\0', + ncow, ncow != 1 ? 's' : '\0'); + } + } + } +} diff --git a/Applications/MWC/cmd/pnmatch.c b/Applications/MWC/cmd/pnmatch.c new file mode 100644 index 00000000..29a37c34 --- /dev/null +++ b/Applications/MWC/cmd/pnmatch.c @@ -0,0 +1,85 @@ + +/* + * pnmatch(string, pattern, unanchored) + * returns 1 if pattern matches in string. + * pattern: + * [c1c2...cn-cm] class of characters. + * ? any character. + * * any # of any character. + * ^ beginning of string (if unanchored) + * $ end of string (if unanchored) + * unanch: + * 0 normal (anchored) pattern. + * 1 unanchored (^$ also metacharacters) + * >1 end unanchored. + * >1 is used internally but should not be used by the user. + */ +pnmatch(s, p, unanch) +register char *s, *p; +{ + register c1; + int c2; + + if (unanch == 1) { + while (*s) + if (pnmatch(s++, p, ++unanch)) + return (1); + return (0); + } + while (c2 = *p++) { + c1 = *s++; + switch(c2) { + case '^': + if (unanch == 2) { + s--; + continue; + } else if (unanch == 0) + break; + else + return (0); + + case '$': + if (unanch) + return (c1 == '\0'); + break; + + case '[': + for (;;) { + c2 = *p++; + if (c2=='\0' || c2==']') + return (0); + if (c2 == '\\' && *p == '-') + c2 = *p++; + if (c2 == c1) + break; + if (*p == '-') + if (c1<=*++p && c1>=c2) + break; + } + while (*p && *p++!=']') + ; + + case '?': + if (c1) + continue; + return(0); + + case '*': + if (!*p) + return(1); + s--; + do { + if (pnmatch(s, p, unanch)) + return (1); + } while(*s++ != '\0'); + return(0); + + case '\\': + if ((c2 = *p++) == '\0') + return (0); + } + if (c1 != c2) + return (0); + } + return(unanch ? 1 : !*s); +} diff --git a/Applications/MWC/cmd/pr.c b/Applications/MWC/cmd/pr.c new file mode 100755 index 00000000..e51d87fa --- /dev/null +++ b/Applications/MWC/cmd/pr.c @@ -0,0 +1,458 @@ +/* + * pr.c + * Print files. + * 7/5/94 + * All references to page length exclude possible margin lines. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define LSIZE 256 /* line size */ +#define LENGTH (66-MARGIN) /* default page length */ +#define WIDTH 80 /* default page width */ +#define TMAR 5 /* five lines of top margin */ +#define BMAR 5 /* five lines of bottom margin */ +#define HEADER 2 /* line # where header appears */ + +#define MARGIN (TMAR+BMAR) + +#define USAGE "Usage: pr [ options ] [ file ...]\n"\ +"Options:\n"\ +"\t+skip\tSkip the first skip pages of input before printing\n"\ +"\t-cols\tPrint the input in cols columns\n"\ +"\t-h\tThe next argument is the header (replaces file name)\n"\ +"\t-ln\tSet page size to n lines (default, 66)\n"\ +"\t-m\tPrint each input file in a separate column\n"\ +"\t-n\tNumber the output lines\n"\ +"\t-sc\tSeparate each column with character c\n"\ +"\t-t\tSuppress top and bottom margins and header\n"\ +"\t-wn\tPage width is set to n columns (default, 80)\n"\ +"A file named `-' means stdin.\n" + +/* + * info on input file streams + * Unless -m is in effect, only f[0] is used. + */ +struct f { + int f_ff; /* '\f' received */ + FILE *f_stream; /* input stream */ +}; + + +int ncol = 1, /* # of columns */ + nskip, /* # pages to skip of each file */ + length = LENGTH, /* page length */ + width = WIDTH, /* page width */ + lno, /* current input line # */ + fwidth; /* field width */ +char schar, /* separator char */ + tflag, /* -t: no header or margins */ + mflag, /* -m: multiple file output */ + nflag, /* -n: line number output */ +*date, /* date string */ +*header, /* -h: header text */ +**lines; /* buffer addr for multicolumn */ + +struct f f[20]; /* input file info */ + +extern int page1(void (*putline) (char *, int)); +extern int page2(void (*putline) (char *, int)); + +int (*page) (void (*fn) (char *, int)) = (void *)page1; + +/* + * read a line + * Simple char processing is done, including tab expansion. getline( ) + * will return an empty line if f_ff is set. Lines are truncated to + * fit the field width. + */ +int getline(struct f *fp, char *lbuf) +{ + int col; + char *p; + int c; + + col = 0; + p = lbuf; + if (feof(fp->f_stream) == 0 && fp->f_ff == 0) { + if (nflag) { + sprintf(p, "%4d: ", ++lno); + p += 6; + } + for (;;) { + switch (c = getc(fp->f_stream)) { + case '\f': + ++fp->f_ff; + case EOF: + if (nflag && (p == &lbuf[6])) + p = lbuf; + break; + case '\n': + break; + case '\r': + continue; + case '\t': + do { + if (p < &lbuf[LSIZE - 1] + && col < fwidth) + *p++ = ' '; + } while (++col & 7); + continue; + case '\b': + if (col) { + --col; + if (p < &lbuf[LSIZE - 1] + && col < fwidth) + *p++ = c; + } + continue; + default: + if (p < &lbuf[LSIZE - 1] && col < fwidth) + *p++ = c; + ++col; + continue; + } + break; + } + + } + *p = '\0'; + return (p - lbuf); +} + + +/* + * write a line, incrementally + * A line of output can be built by successive calls to putl( ). A line + * address of 0 puts the newline. Simple char processing is done, + * including tab optimization. Each line segment is padded to the + * field width, unless a there is a field separator char. + */ +void putl(char *lbuf, int schr) +{ + char *p; + int c, nextxcol; + static int col, xcol; + + if ((p = lbuf) == NULL) { + col = 0; + xcol = 0; + putchar('\n'); + return; + } + nextxcol = xcol + fwidth; + + for (;;) { + if ((c = *p++) == '\0') { + if ((c = schr) == '\0') + break; + schr = 0; + --p; + nextxcol = xcol + 1; + } + if (c == ' ') { + ++xcol; + continue; + } + if (c == '\b') { + --xcol; + continue; + } + while ((col | 7) + 1 <= xcol) { + col = (col | 7) + 1; + putchar('\t'); + } + while (col < xcol) { + ++col; + putchar(' '); + } + while (col > xcol) { + --col; + putchar('\b'); + } + putchar(c); + xcol = ++col; + } + + xcol = nextxcol; +} + + +/* + * throw away output line + * Used when skipping the first pages of input. + */ +void nop(char *lbuf, int schr) +{ + +} + +/* + * print from open input streams + * Control output of pages, perhaps provide header/footer margins and title. + * The paging routine is expected to give an eof warning on the last page. + */ +void print(char *file) +{ + int i, pg, eof; + + for (pg = 1; pg <= nskip; ++pg) + if ((*page) (nop)) + return; + + do { + if (tflag == 0) + for (i = 0; i < TMAR; ++i) { + if (i != HEADER) { + putchar('\n'); + continue; + } + printf + ("%.12s%.5s %s Page %d, line %ld\n", + date + 4, date + 19, + header ? header : file, pg, + (long) (pg - + 1) * (mflag ? 1 : ncol) * + length + 1); + } + eof = (*page) (putl); + if (tflag == 0) + for (i = 0; i < BMAR; ++i) + putchar('\n'); + } while (++pg, eof == 0); +} + + +/* + * open input file + * Failure to open is fatal. openf( "-") returns stdin. + */ +FILE *openf(char *file) +{ + FILE *stream; + + lno = 0; + if (file[0] == '-' && file[1] == '\0') + return (stdin); + if ((stream = fopen(file, "r")) == NULL) + if (errno == EMFILE) + errx(1, "too many files for -m"); + else + err(1, "cannot open \"%s\"", file); + return (stream); +} + +/* + * page one stream per column + * This handles all cases of one column per page. + * Formfeed advances output of that stream to the next page. + * Eof is only indicated when all streams give this condition. + */ +int page1(void (*putline) (char *, int)) +{ + int i, j; + char lbuf[LSIZE]; + + for (i = 0; i < length; ++i) { + for (j = 0; j < ncol; ++j) { + getline(&f[j], lbuf); + (*putline) (lbuf, j < ncol - 1 ? schar : 0); + } + (*putline) ((char *) 0, 0); + } + + i = 0; + for (j = 0; j < ncol; ++j) { + f[j].f_ff = 0; + if (feof(f[j].f_stream)) + ++i; + else if (ungetc(getc(f[j].f_stream), f[j].f_stream) == EOF) + ++i; + } + return (i == j); +} + + +/* + * page one stream across multiple columns + * The first `ncol'-1 columns of text are buffered; the last is simply + * read as needed. Formfeeds advance output to the next column. + */ + +int page2(void (*putline) (char *, int)) +{ + int i, j, k; + char lbuf[LSIZE]; + + for (i = 0; i < (ncol - 1) * length; ++i) { + if ((i % length) == 0) + f[0].f_ff = 0; + if (k = getline(&f[0], lbuf)) { + if ((lines[i] = malloc(k + 1)) == NULL) + errx(1, "out of space"); + strcpy(lines[i], lbuf); + } else + lines[i] = NULL; + } + f[0].f_ff = 0; + + for (i = 0; i < length; ++i) { + for (j = 0; j < ncol - 1; ++j) { + k = j * length + i; + if (lines[k]) { + (*putline) (lines[k], schar); + free(lines[k]); + } else + (*putline) ("", schar); + } + getline(&f[0], lbuf); + (*putline) (lbuf, 0); + (*putline) ((char *) 0, 0); + } + + if (feof(f[0].f_stream)) + return (1); + if (ungetc(getc(f[0].f_stream), f[0].f_stream) == EOF) + return (1); + return (0); +} + + +/* + * initialize & get options + * Flags are recognized up to the first file name. If multi-column (-N), + * allocate line array. If printing multiple files (-m), open all files. + * There are two paging algorithms: one file per column (page1), and + * many columns per file (page2). The latter requires page buffering. + * init( ) makes this selection. + */ +char **init(int ac, char **av) +{ + int mar = MARGIN; + static char obuf[BUFSIZ]; + time_t tvec; + + setbuf(stdout, obuf); + + while (++av, --ac) { + if (av[0][0] == '+') + if ((nskip = atoi(&av[0][1])) <= 0) + errx(1, "bad skip"); + else + continue; + if (av[0][0] != '-') + break; + switch (av[0][1]) { + case '\0': + break; + case 'l': + length = atoi(&av[0][2]) - mar; + continue; + case 'w': + width = atoi(&av[0][2]); + continue; + case 'h': + if (av[0][2]) + header = &av[0][2]; + else { + if (--ac <= 0) + errx(1, "missing header arg"); + header = (++av)[0]; + } + continue; + case 's': + if ((schar = av[0][2]) == '\0') + schar = '\t'; + continue; + case 'm': + ++mflag; + continue; + case 't': + ++tflag; + length += mar; + mar = 0; + continue; + case 'n': + ++nflag; + continue; + default: + /* FIXME: trap ncol < 1 */ + if ('0' <= av[0][1] && av[0][1] <= '9') + ncol = atoi(&av[0][1]); + else { + fprintf(stderr, USAGE); + exit(1); + } + continue; + } + break; + + } + + f[0].f_stream = stdin; + if (mflag && av[0]) { + ncol = 0; + do { + f[ncol++].f_stream = openf(av++[0]); + } while (av[0]); + } + + /* + * check that all options jive + */ + if (length <= 0) + if (length == -mar) { /* gunja artifice */ + length = 1; + ++tflag; + } else + errx(1, "length too small"); + fwidth = width / ncol; + if (schar) + --fwidth; + if (fwidth <= 0 || (nflag && fwidth < 10)) + errx(1, "width too small"); + if (fwidth >= LSIZE - 1) + errx(1, "too wide"); + /* FIXME: maths overflow */ + if (ncol > 1 && mflag == 0) { + if ((lines = + malloc((ncol - 1) * length * sizeof(char *))) == NULL) + errx(1, "insufficient core"); + page = (void *)page2; /* Hack cast for SDCC */ + } + + time(&tvec); + date = ctime(&tvec); + + return (av); +} + + +/* + * paginate files to standard output + * If no files are given, use standard input. The file name "-" also + * means standard input. + */ +int main(int argc, char *argv[]) +{ + argv = init(argc, argv); + + if (*argv) + while (*argv) { + f[0].f_stream = openf(*argv); + print(*argv++); + fclose(f[0].f_stream); + } else + print(""); + + return (0); +} + +/* end of pr.c */ diff --git a/Applications/MWC/cmd/tar.c b/Applications/MWC/cmd/tar.c new file mode 100644 index 00000000..02e28094 --- /dev/null +++ b/Applications/MWC/cmd/tar.c @@ -0,0 +1,1108 @@ +/* + * Tape archive + * tar [0-7bcflmrtuvwx]+ [blocks] [archive] pathname* + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define S_PERM 07777 /* should be in stat.h */ + + +/* FIMXE: Non portable */ +#define DIRSIZ 30 +struct direct { + uint16_t d_ino; + uint8_t d_name[DIRSIZ]; +}; + +/* Possible actions for do_checksum_error(). */ +#define CLEAR 0 /* Print all pending messages. */ +#define ADD 1 /* Add another pending message. */ + +#define MAXBLK 20 +#define roundup(n, r) (((n)+(r)-1)/(r)) + +typedef struct dirhd_t { + dev_t t_dev; + ino_t t_ino; + unsigned short t_nlink, t_mode, t_uid, t_gid; + off_t t_size; + time_t t_mtime; + char *t_name; + struct dirhd_t *t_cont[]; +} dirhd_t; + +typedef union tarhd_t { + struct { + char th_name[100], + th_mode[8], + th_uid[8], + th_gid[8], + th_size[12], + th_mtime[12], + th_check[8], th_islink, th_link[100], th_pad[255]; + } th_info; + char th_data[BUFSIZ]; +} tarhd_t; + +typedef unsigned short flag_t; /* fastest type for machine */ + +flag_t linkmsg = 0, /* message if not all links found */ + modtime = 1, /* restore modtimes */ + verbose = 0; +int unixbug = 0; /* avoid bug in U**X tar */ +FILE *whether = (FILE *) NULL, /* ask about each file */ + *tarfile; +char tapedev[10] = { '\0' }; +char *archive = &tapedev[0]; +unsigned short blocking = 1; /* blocking factor */ +struct utimbuf oldtime; /* for utime */ +time_t recently; /* set to 6 months ago for tv key */ + +void fatal(char *args, ...); +dirhd_t *newdirhd(char *name, unsigned short len); +void table(dirhd_t * args); +void extract(dirhd_t * args); +dirhd_t *update(char *name, dirhd_t * args, tarhd_t * header); +void append(char *name, dirhd_t * args); +dirhd_t *research(char *name, dirhd_t * args); +int argcont(dirhd_t * args, char *name, int ret); +int makepath(char *pathname); +int mkparent(char *pathname); +int recreate(char *pathname, unsigned short mode); +int create(char *pathname, unsigned short mode); +int disallow(char function, char *pathname); +tarhd_t *readhdr(void); +void skipfile(tarhd_t * header); +void writehdr(char *name, dirhd_t * args, char *link); +void writefil(char *name, off_t size); +tarhd_t *readblk(void); +void ungetblk(void); +tarhd_t *writeblk(void); +void flushtar(void); +void filelink(dev_t dev, ino_t ino, unsigned short nlink, char *link); +char *havelink(dev_t dev, ino_t ino, flag_t flag); +void misslink(void); +int getoct(char *cp); +long getoctl(char *cp); +void putoct(char *str, unsigned short val); +void putoctl(char *str, unsigned long val); +int checksum(char *str, int len); +int contains(char *dname, char *fname); +void do_checksum_error(char *name, int action); +void scan_for_next(void); + +int main(int argc, char *argv[]) +{ + char *key, unit = '\0', function = 0, deffunc, prefix[101] = { '\0' }; + unsigned short arg = 2; + dirhd_t *args; + + if (argc < 2) { + fprintf(stderr, + "Usage: %s [crtux][0-7bflmvw] [blocks] [archive] pathname*\n", + argv[0]); + exit(-1); + } + for (key = argv[1]; *key != '\0'; key++) + switch (*key) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + unit = *key; + deffunc = 't'; + continue; + case 'b': + if (argc <= arg) + fatal("missing blocking factor"); + else if ((blocking = atoi(argv[arg++])) <= 0 + || blocking > MAXBLK) + fatal("illegal blocking factor"); + deffunc = 'r'; + continue; + case 'f': + if (argc <= arg) + fatal("missing archive name"); + else + archive = argv[arg++]; + deffunc = 't'; + continue; + case 'l': + linkmsg = 1; + deffunc = 'r'; + continue; + case 'm': + modtime = 0; + deffunc = 'x'; + continue; + case 'v': + verbose = 1; + deffunc = 't'; + continue; + case 'U': + unixbug = 1; + deffunc = 't'; + continue; + case 'w': + whether = fopen("/dev/tty", "r+w"); + deffunc = 'x'; + continue; + case 'c': + case 'r': + case 't': + case 'u': + case 'x': + if (function) + fatal + ("keys c, r, t, u, x are mutually exclusive"); + else + function = *key; + continue; + default: + fatal("illegal key '%c'", *key); + } + if (!function) + function = deffunc; + oldtime.actime = time((time_t *) 0); + if (verbose && function == 't') + recently = oldtime.actime - 6 * ((time_t) 30 * 24 * 60 * 60); + + /* construct descriptors for args given */ + + args = (dirhd_t *) malloc(sizeof(dirhd_t) + + (argc - arg) * sizeof(dirhd_t *)); + if (args == NULL) + fatal("out of memory"); + args->t_mode = S_IFDIR; + args->t_nlink = 0; + for (; arg < argc; arg++) { + dirhd_t *argp; + + if ((argp = + newdirhd(argv[arg], strlen(argv[arg]))) == NULL) + fprintf(stderr, "Tar: %s: out of memory\n", + argv[arg]); + else + args->t_cont[args->t_nlink++] = argp; + } + + /* open archive file */ + + if (*archive == '\0') + sprintf(archive, "/dev/%smt%c", blocking == 1 ? "" : "r", + unit); + if (function == 't' || function == 'x') + if (strcmp(archive, "-") == 0) + tarfile = stdin; + else + tarfile = fopen(archive, "r"); + else if (function == 'c') + if (strcmp(archive, "-") == 0) + tarfile = stdout; + else + tarfile = fopen(archive, "w"); + else + tarfile = fopen(archive, "r+w"); + if (tarfile == NULL) + err(1, "Tar: 1: %s", archive); + setbuf(tarfile, NULL); + + /* perform required function */ + + switch (function) { + case 't': + table(args); + break; + case 'x': + extract(args); + break; + case 'r': + while (readblk() != NULL); + goto doappend; + case 'u': + if ((args = update(prefix, args, readhdr())) == NULL) + break; + case 'c': + doappend: + append(prefix, args); + flushtar(); + } + /* Flush any remaining checksum messages. */ + do_checksum_error(NULL, CLEAR); + + exit(errno); +} + +void fatal(char *args, ...) +{ + /* Flush any remaining checksum messages. */ + do_checksum_error(NULL, CLEAR); + + fprintf(stderr, "tar: %r\n", &args); + exit(-1); +} + +dirhd_t *newdirhd(char *name, unsigned short len) +{ + dirhd_t *dp; + + if ((dp = (dirhd_t *) malloc(sizeof(dirhd_t))) != NULL + && (dp->t_name = malloc(len + 1)) == NULL) { + free(dp); + dp = NULL; + } + if (dp != NULL) { + dp->t_nlink = 0; + strncpy(dp->t_name, name, len); + dp->t_name[len] = '\0'; + } + return (dp); +} + +void table(dirhd_t * args) +{ + tarhd_t *header; + + for (; (header = readhdr()) != NULL; skipfile(header)) { + if (argcont(args, header->th_info.th_name, 0) < 0) + continue; + if (verbose) { + unsigned short mode = + getoct(header->th_info.th_mode); + unsigned short uid = + getoct(header->th_info.th_uid), gid = + getoct(header->th_info.th_gid); + off_t size = getoctl(header->th_info.th_size); + time_t mtime = getoctl(header->th_info.th_mtime); + char *timestr = ctime(&mtime); + + if (header->th_info. + th_name[strnlen(header->th_info.th_name, 100) - + 1] + == '/') + putchar('d'); + else + putchar('-'); + printf("%c%c%c", + mode & S_IROTH ? 'r' : '-', + mode & S_IWOTH ? 'w' : '-', + mode & S_ISUID ? 's' : + mode & S_IXOTH ? 'x' : '-'); + printf("%c%c%c", + mode & S_IRGRP ? 'r' : '-', + mode & S_IWGRP ? 'w' : '-', + mode & S_ISGID ? 's' : + mode & S_IXGRP >> 3 ? 'x' : '-'); + printf("%c%c%c", + mode & S_IRUSR >> 6 ? 'r' : '-', + mode & S_IWUSR >> 6 ? 'w' : '-', + mode & S_ISVTX ? 't' : + mode & S_IXUSR >> 6 ? 'x' : '-'); + printf("%3d %3d %6ld %.11s%.5s ", + gid, + uid, + size, + timestr, + mtime > + recently ? timestr + 11 : timestr + 19); + } + printf("%.100s", header->th_info.th_name); + if (header->th_info.th_islink) + if (verbose) + printf("\n%33s link to %s\n", "", + header->th_info.th_link); + else + printf(" link to %s\n", + header->th_info.th_link); + else + putchar('\n'); + } +} + +void extract(dirhd_t * args) +{ + tarhd_t *header; + short skipping = 0; + + while ((header = readhdr()) != NULL) { + char name[101]; + unsigned short namelen = + strnlen(header->th_info.th_name, 100), mode, uid, gid; + + if (!skipping + || contains(name, header->th_info.th_name) >= 0) { + strncpy(name, header->th_info.th_name, namelen); + name[namelen--] = '\0'; + if ((skipping = argcont(args, name, 0)) >= 0) + skipping = disallow('x', name); + } + switch (skipping) { + case -1: + skipping = 0; + case 1: + skipfile(header); + continue; + } + if (verbose) + printf("x %s\n", name); + mode = getoct(header->th_info.th_mode); + uid = getoct(header->th_info.th_uid); + gid = getoct(header->th_info.th_gid); + oldtime.modtime = getoctl(header->th_info.th_mtime); + if (name[namelen] == '/') { + name[namelen] = '\0'; + makepath(name); + chmod(name, mode); + name[namelen] = '/'; + } else if (header->th_info.th_islink == '\0') { + int fd = recreate(name, mode); + off_t size = getoctl(header->th_info.th_size); + + for (; size > 0; size -= sizeof(tarhd_t)) { + int my_size; + + /* Handle unexpected EOF - mods by vlad@kiev.mwc.com */ + if ((header = readblk()) == NULL) { + fprintf(stderr, + "Unexpected end of the file %s\n", + name); + break; + } + if (size > sizeof(tarhd_t)) + my_size = sizeof(tarhd_t); + else + my_size = (int) size; + if (fd >= 0 && (write(fd, header->th_data, + my_size) != my_size)) + + warn("Tar: 2: %s", name); + } + if (fd >= 0) + close(fd); + } else { + struct stat statbuf; + flag_t xlink = 1; + + if (stat(header->th_info.th_link, &statbuf) < 0) + close(recreate + (header->th_info.th_link, mode)); + else if (havelink + (statbuf.st_dev, statbuf.st_ino, 0) + != NULL) + xlink = 0; + if (xlink) + fprintf(stderr, "Tar: Must extract %s\n", + header->th_info.th_link); + unlink(name); + mkparent(name); + if (link(header->th_info.th_link, name) < 0) + fprintf(stderr, + "Tar: Can't link %s to %s\n", name, + header->th_info.th_link); + } + if (modtime) + utime(name, &oldtime); + chown(name, uid, gid); + } +} + +dirhd_t *update(char *name, dirhd_t * args, tarhd_t * header) +{ + unsigned short namelen = strlen(name); + flag_t donedir = 0; + + if (header == NULL) + return (args); + if (args->t_nlink == 0 && (args = research(name, args)) == NULL) + return (NULL); + do + switch (args->t_mode & S_IFMT) { + short arg; + dirhd_t *argp; + + case S_IFREG: + if (args->t_mtime <= + getoctl(header->th_info.th_mtime)) + args->t_nlink = 0; + skipfile(header); + continue; + case S_IFDIR: + if (namelen != 0 && !donedir) { + name[namelen] = '/'; + donedir++; + } + if ((arg = + argcont(args, + header->th_info.th_name + namelen + + donedir, -1)) + >= 0) { + strcpy(name + namelen + donedir, + (argp = args->t_cont[arg])->t_name); + if ((args->t_cont[arg] = + update(name, argp, header)) + == NULL) + args->t_cont[arg] + = args->t_cont[--args-> + t_nlink]; + } else + skipfile(header); + name[namelen + donedir] = '\0'; + } while ((header = readhdr()) != NULL + && contains(name, header->th_info.th_name)); + if (header != NULL) + ungetblk(); + if (args->t_nlink == 0) { + if (namelen != 0) + free(args->t_name); + free((char *) args); + return (NULL); + } else + return (args); +} + +void append(char *name, dirhd_t * args) +{ + unsigned short namelen; + unsigned short arg; + dirhd_t *argp; + + if ((namelen = strlen(name)) != 0 && disallow('a', name)) + return; + if (args->t_nlink == 0 && (args = research(name, args)) == NULL) + return; + switch (args->t_mode & S_IFMT) { + case S_IFDIR: + if (namelen != 0) { + name[namelen++] = '/'; + name[namelen] = '\0'; + writehdr(name, args, ""); + } + for (arg = 0; arg < args->t_nlink; arg++) { + strcpy(name + namelen, + (argp = args->t_cont[arg])->t_name); + append(name, argp); + } + break; + case S_IFREG: + if (args->t_nlink != 1) { + char *link; + + if ((link = havelink(args->t_dev, + args->t_ino, 1)) != NULL) { + writehdr(name, args, link); + break; + } else { + filelink(args->t_dev, + args->t_ino, args->t_nlink, name); + } + } + writehdr(name, args, ""); + writefil(name, args->t_size); + } + if (namelen != 0) + free(args->t_name); + free((char *) args); +} + +dirhd_t *research(const char *name, dirhd_t * args) +{ + unsigned short namelen; + short nfile; + int fd; + struct stat statbuf; + + if ((namelen = strlen(name)) == 0) + name = "."; + if (stat(name, &statbuf) < 0) { + warn("Tar: 3: %s", name); + return (args); + } + args->t_dev = statbuf.st_dev; + args->t_ino = statbuf.st_ino; + args->t_nlink = statbuf.st_nlink; + args->t_uid = statbuf.st_uid; + args->t_gid = statbuf.st_gid; + args->t_mtime = statbuf.st_mtime; + switch ((args->t_mode = statbuf.st_mode) & S_IFMT) { + case S_IFREG: + args->t_size = statbuf.st_size; + return (args); + default: + args->t_size = 0; + return (args); + case S_IFDIR: + args->t_size = 0; + } + if ((nfile = + (short) (statbuf.st_size / sizeof(struct direct))) > 2) { + args = + (dirhd_t *) realloc((char *) args, + sizeof(dirhd_t) + (nfile - + 2) * + sizeof(dirhd_t *)); + if (args == NULL) { + fprintf(stderr, "Tar: %s: out of memory\n", name); + return (NULL); + } + } + args->t_nlink = 0; + if ((fd = open(name, 0)) < 0) { + warn("Tar: 4: %s", name); + return (args); + } + /* FIXME: readdir ? */ + for (; nfile > 0; --nfile) { + struct direct dir_ent; + unsigned short arglen; + dirhd_t *argp; + + switch (read(fd, (char *) &dir_ent, sizeof(struct direct))) { + case -1: + warn("Tar: 5: %s", name); + case 0: + break; + default: + if (dir_ent.d_ino == 0 + || strcmp(dir_ent.d_name, ".") == 0 + || strcmp(dir_ent.d_name, "..") == 0) + continue; + else if ((arglen = strnlen(dir_ent.d_name, DIRSIZ)) + + namelen > 99) + fprintf(stderr, + "Tar: %s/%.*s: name too long\n", + name, DIRSIZ, dir_ent.d_name); + else if ((argp = newdirhd(dir_ent.d_name, arglen)) + == NULL) + fprintf(stderr, + "Tar: %s/%.*s: out of memory\n", + name, DIRSIZ, dir_ent.d_name); + else + args->t_cont[args->t_nlink++] = argp; + continue; + } + break; + } + close(fd); + return (args); +} + +int argcont(dirhd_t * args, char *name, int ret) +{ + unsigned short arg; + + if (args->t_nlink == 0) + return (ret); + else + for (arg = 0; arg < args->t_nlink; arg++) + if (contains(args->t_cont[arg]->t_name, name)) + return (arg); + return (-1); +} + +/* + * Create file system paths + */ + +int makepath(char *pathname) +{ + struct stat statbuf; + int err; + + if (stat(pathname, &statbuf) == 0) + if ((statbuf.st_mode & S_IFMT) == S_IFDIR) + return (0); + else + errno = ENOTDIR; + else if (errno == ENOENT) { + errno = 0; + if ((err = mkparent(pathname)) != 0) + return err; + return mkdir(pathname, 0777); + } + warn("Tar: 6: %s", pathname); + return 1; +} + +int mkparent(char *pathname) +{ + char *pathend = &pathname[strlen(pathname)]; + + while (pathend > pathname && *--pathend != '/'); + if (pathend > pathname) { + *pathend = '\0'; + errno = makepath(pathname); + *pathend = '/'; + } else + errno = 0; + return (errno); +} + +int recreate(char *pathname, unsigned short mode) +{ + int fd; + + if ((fd = create(pathname, mode)) < 0 + && (errno != ENOENT + || mkparent(pathname) == 0 + && (fd = create(pathname, mode)) < 0)) + warn("Tar: 8: %s", pathname); + errno = 0; + return (fd); +} + +int create(char *pathname, unsigned short mode) +{ + int fd; + + unlink(pathname); + if ((fd = creat(pathname, mode)) >= 0) { + struct stat statbuf; + + fstat(fd, &statbuf); + filelink(statbuf.st_dev, statbuf.st_ino, + statbuf.st_nlink, pathname); + } + return (fd); +} + +/* + * Ask about current action + */ + +int disallow(char function, char *pathname) +{ + while (whether) { + int c1, c; + + fprintf(whether, "%c %s? ", function, pathname); + for (c1 = c = getc(whether); c != EOF && c != '\n'; + c = getc(whether)); + switch (c1) { + case EOF: + case 'x': + if (function == 'a') + flushtar(); + exit(errno); + case '\n': + case 'n': + case 'N': + return (1); + case 'y': + case 'Y': + return (0); + } + } + return (0); +} + +/* + * High level I/O routines + */ + +tarhd_t *readhdr(void) +{ + tarhd_t *header; + + while ((header = readblk()) != NULL) { + if (header->th_info.th_name[0] == '\0') + if (unixbug) { + ungetblk(); + return (NULL); + } else + continue; + else { + int check = getoct(header->th_info.th_check); + + strncpy(header->th_info.th_check, " ", + sizeof(header->th_info.th_check)); + if (checksum(header->th_data, sizeof(tarhd_t)) != + check) { + do_checksum_error(header->th_info.th_name, + ADD); + } else { + do_checksum_error(NULL, CLEAR); + break; + } + } + } + /* We shouldn't have to check EVERY header, but if we do it here + * it gets done for every routine. + * Newer tar programs use the first eight bytes of the + * pad field as magic to indicate the type of archive. + * With old tar archives, these eight characters should be + * NUL. We'll only check the first one. + */ + /* Check to see if this is a v7 style tar archive. */ + if ((char) 0 != header->th_info.th_pad[0]) { + fatal("%.8s archive, not v7 archive.", + header->th_info.th_pad); + + } + return (header); +} + +void skipfile(tarhd_t * header) +{ + off_t size; + + if (header->th_info.th_islink) + return; + for (size = getoctl(header->th_info.th_size); + size > 0 && readblk() != NULL; size -= sizeof(tarhd_t)); +} + +void writehdr(char *name, dirhd_t * args, char *link) +{ + tarhd_t *header = writeblk(); + + if (verbose) + fprintf(stderr, "a %s", name); + strncpy(header->th_info.th_name, name, + sizeof(header->th_info.th_name)); + putoct(header->th_info.th_mode, args->t_mode & S_PERM); + putoct(header->th_info.th_uid, args->t_uid); + putoct(header->th_info.th_gid, args->t_gid); + putoctl(header->th_info.th_size, args->t_size); + putoctl(header->th_info.th_mtime, args->t_mtime); + strncpy(header->th_info.th_check, " ", + sizeof(header->th_info.th_check)); + if (*link == '\0') { + header->th_info.th_islink = 0; + if (verbose) + if (args->t_size == 0) + putc('\n', stderr); + else + fprintf(stderr, " %ld block%s\n", + roundup(args->t_size, BUFSIZ), + args->t_size <= BUFSIZ ? "" : "s"); + } else { + header->th_info.th_islink = '1'; + if (verbose) + fprintf(stderr, " link to %s\n", link); + } + strncpy(header->th_info.th_link, link, + sizeof(header->th_info.th_link)); + strncpy(header->th_info.th_pad, "", + sizeof(header->th_info.th_pad)); + putoct(header->th_info.th_check, + checksum(header->th_data, sizeof(tarhd_t))); +} + +void writefil(char *name, off_t size) +{ + int fd; + tarhd_t *header; + + if ((fd = open(name, 0)) < 0) { + warn("Tar: 9: %s", name); + } + for (; size > 0; size -= sizeof(tarhd_t)) { + header = writeblk(); + if (fd >= 0) + read(fd, header->th_data, sizeof(tarhd_t)); + else + strncpy(header->th_data, "", sizeof(tarhd_t)); + } + if (fd >= 0) + close(fd); +} + +/* + * Tape I/O, with record blocking if specified + */ + +tarhd_t buffer[MAXBLK], *current = &buffer[0]; + +tarhd_t *readblk(void) +{ + static unsigned short blocks = 0; + + if (feof(tarfile)) + return (NULL); + if (current == &buffer[blocks]) { + current = &buffer[0]; + blocks = fread((char *) buffer, + sizeof(tarhd_t), MAXBLK, tarfile); + if (ferror(tarfile)) { + err(1, "Tar: 10: %s", archive); + } else if (feof(tarfile)) { + /* This is a hack. We should actually find + * out why returning NULL is not good enough. + * + * Really a pain without a symbolic debugger! + */ + exit(0); + } + } + return (current++); +} + +void ungetblk(void) +{ + --current; +} + +tarhd_t *writeblk(void) +{ + if (current == &buffer[blocking]) { + current = &buffer[0]; + fwrite((char *) buffer, sizeof(tarhd_t), blocking, + tarfile); + if (ferror(tarfile) || feof(tarfile)) { + err(1, "Tar: 11: %s", archive); + } + } + return (current++); +} + +void flushtar(void) +{ + tarhd_t *header; + + while ((header = writeblk()) != &buffer[0]) + strncpy(header->th_data, "", sizeof(tarhd_t)); + if (linkmsg) + misslink(); +} + +/* + * Keep track of files by ino in hash table + */ + +typedef struct link_t { + dev_t t_dev; + ino_t t_ino; + unsigned short t_nlink; + struct link_t *t_next; + char t_link[0]; +} link_t; + +#define NHASH 64 + +link_t *linklist[NHASH]; + +void filelink(dev_t dev, ino_t ino, unsigned short nlink, char *link) +{ + unsigned short id = ino % NHASH; + link_t *lp; + + /* FIXME OVERFLOW ? */ + lp = (link_t *) malloc(sizeof(link_t) + strlen(link) + 1); + if (lp == NULL) + fprintf(stderr, "Tar: %s: note: link info lost\n", link); + else { + lp->t_next = linklist[id]; + linklist[id] = lp; + lp->t_dev = dev; + lp->t_ino = ino; + lp->t_nlink = nlink; + strcpy(lp->t_link, link); + } +} + +char *havelink(dev_t dev, ino_t ino, flag_t flag) +{ + link_t *lp; + + for (lp = linklist[ino % NHASH]; lp != NULL; lp = lp->t_next) { + if (lp->t_ino == ino && lp->t_dev == dev) { + if (flag) + --lp->t_nlink; + return (lp->t_link); + } + } + return (NULL); +} + +void misslink(void) +{ + unsigned short ino; + + for (ino = 0; ino < NHASH; ino++) { + link_t *lp; + + for (lp = linklist[ino]; lp != NULL; lp = lp->t_next) { + short nlink; + + if ((nlink = lp->t_nlink - 1) != 0) + fprintf(stderr, + "Tar: missed %d link%s to %s\n", + nlink, nlink == 1 ? "" : "s", + lp->t_link); + } + } +} + +/* + * Read unsigned octal numbers + */ + +int getoct(char *cp) +{ + int val = 0; + signed char c; /* Force signed to avoid SDCC bug */ + + while (*cp == ' ') + cp++; + while ((c = *cp++ - '0') >= 0 && c <= 7) + val = val << 3 | c; + return (val); +} + +long getoctl(char *cp) +{ + long val = 0; + signed char c; /* Ditto */ + + while (*cp == ' ') + cp++; + while ((c = *cp++ - '0') >= 0 && c <= 7) + val = val << 3 | c; + return (val); +} + +/* + * Write unsigned octal numbers + * in fixed format + */ + +void putoct(char *str, unsigned short val) +{ + char *cp; + + *(cp = &str[7]) = '\0'; + *--cp = ' '; + *--cp = (val & 07) + '0'; + while (cp != str) + *--cp = (val >>= 3) ? (val & 07) + '0' : ' '; +} + +void putoctl(char *str, unsigned long val) +{ + char *cp; + + *(cp = &str[11]) = ' '; + *--cp = (val & 07) + '0'; + while (cp != str) + *--cp = (val >>= 3) ? (val & 07) + '0' : ' '; +} + +/* + * Compute checksum of string + */ +int checksum(char *str, int len) +{ + int check = 0; + + if (len) + do + check += *str++; + while (--len); + return (check); +} + +/* + * Return -1 if dname is a directory prefix of fname + * 0 if no match + * 1 if complete match + */ +int contains(char *dname, char *fname) +{ + if (*dname == '\0') + return (-1); + while (*dname != '\0') + if (*dname++ != *fname++) + return (0); + if (*fname == '\0') + return (1); + else if (*fname == '/' || *--fname == '/') + return (-1); + else + return (0); +} + +/* Handle the processing of a checksum error. + * This is special because sometimes end of archive will look like + * a lot of checksum errors. + */ +void do_checksum_error(char *name, int action) +{ +#define MSG_SIZE (100 + sizeof("Tar: %.100s: bad checksum\n") + 1) +#define MAX_CHECKERR 5 /* Give up after 5 consecutive checksum errors. */ + + static int error_count = 0; /* Number of checksums we've seen. */ + static char *msg_list = NULL; /* Error message we've queued. */ + char tmp_buf[MSG_SIZE]; /* Someplace to build new errors. */ + + + switch (action) { + case ADD: + /* If there have been too many checksum errors, or if + * we can't allocate more memory for more error messages, + * throw away the existing messages, and look for another + * valid block. + */ + if (++error_count > MAX_CHECKERR || + (msg_list = (char *) realloc(msg_list, + strlen(msg_list) + + MSG_SIZE + 1)) == NULL) { + fprintf(stderr, + "Tar: %s: This doesn't look like a tar archive.\n", + archive); + free(msg_list); + msg_list = 0; + error_count = 0; + fprintf(stderr, "Tar: Scanning for next file.\n"); + scan_for_next(); + } + + sprintf(tmp_buf, "Tar: %.100s: bad checksum\n", name); + strcat(msg_list, tmp_buf); + break; + case CLEAR: + if (NULL != msg_list) { + fprintf(stderr, "%s", msg_list); + free(msg_list); + msg_list = NULL; + } + error_count = 0; + break; + } /* switch (action) */ +} /* do_checksum_error() */ + +/* Find the next valid block. */ + +void scan_for_next(void) +{ + int check; + tarhd_t *header; + + while ((header = readblk()) != NULL) { + check = getoct(header->th_info.th_check); + + strncpy(header->th_info.th_check, " ", + sizeof(header->th_info.th_check)); + if (checksum(header->th_data, sizeof(tarhd_t)) == check) { + /* Found a good header! */ + ungetblk(); + return; + } + } /* while (more blocks to read) */ + /* If we got here, it means we ran out of things to read. */ + fatal("no more valid blocks"); + +} /* scan_for_next() */ diff --git a/Applications/MWC/cmd/test.c b/Applications/MWC/cmd/test.c new file mode 100755 index 00000000..61018aec --- /dev/null +++ b/Applications/MWC/cmd/test.c @@ -0,0 +1,627 @@ +/* + * New, improved version of the "test" utility, hopefully P1003.2 compliant. + * + * "test" has represented a problem to implementors because of the possibility + * of aliasing between operators and operands; while it is possible to resolve + * this ambiguity with conventional tools, the resulting program is rather + * subtle and easily broken. + * + * The observation the underpins this implementation is that the "test" input + * is more accurately parsed /from the right/ than from the left. Because the + * last element of a form is always an operand rather than an operator, and + * because unary and binary primaries are lexically distinct, it is possible + * to create an unambiguous parse with a single right->left scan of the input. + * + * Parentheses are a unique problem, however. The easiest way to support them + * is to say that parentheses are matched only if they are not consumed by + * any operators, i.e. \( "a" \) would fail to match any operators and so the + * parenthesis would match. + */ + +#include +#include +#include +#include +#include + +#define WRITESTR(s) write (2, s, strlen (s)) + +enum { + SYNTAX_ERROR = 2 +}; + + +typedef int (*compare_p) (long left, long right); +typedef int (*file_binop_p) (const struct stat * left, + const struct stat * right); +typedef int (*log_binop_p) (int left, int right); +typedef int (*string_unop_p) (char *arg); +typedef int (*file_unop_p) (struct stat * stat); + +/* + * Miscellaneous comparison operators. + */ + +#define CMP_OP2(name) static int name (long arg1, long arg2) + +CMP_OP2(cmp_eq) +{ + return arg1 != arg2; +} + +CMP_OP2(cmp_neq) +{ + return arg1 == arg2; +} + +CMP_OP2(cmp_lt) +{ + return arg1 >= arg2; +} + +CMP_OP2(cmp_gt) +{ + return arg1 <= arg2; +} + +CMP_OP2(cmp_le) +{ + return arg1 > arg2; +} + +CMP_OP2(cmp_ge) +{ + return arg1 < arg2; +} + + +/* + * Miscellaneous file operators. + */ + +#define FILE_BINOP(name) \ + static int name (const struct stat * left, const struct stat * right) + +FILE_BINOP(file_eq) +{ + return left->st_dev != right->st_dev + || left->st_ino != right->st_ino; +} + +FILE_BINOP(file_newer) +{ + return left->st_mtime <= right->st_mtime; +} + +FILE_BINOP(file_older) +{ + return left->st_mtime >= right->st_mtime; +} + +#define FILE_UNOP(name) static int name (const struct stat * stat) + +FILE_UNOP(file_exists) +{ + return 0; +} + +FILE_UNOP(file_blockspecial) +{ + return (stat->st_mode & S_IFMT) != S_IFBLK; +} + +FILE_UNOP(file_charspecial) +{ + return (stat->st_mode & S_IFMT) != S_IFCHR; +} + +FILE_UNOP(file_directory) +{ + return (stat->st_mode & S_IFMT) != S_IFDIR; +} + +FILE_UNOP(file_pipe) +{ + return (stat->st_mode & S_IFMT) != S_IFIFO; +} + +FILE_UNOP(file_regular) +{ + return (stat->st_mode & S_IFMT) != S_IFREG; +} + +FILE_UNOP(file_setgid) +{ + return (stat->st_mode & S_ISGID) == 0; +} + +FILE_UNOP(file_setuid) +{ + return (stat->st_mode & S_ISUID) == 0; +} + +FILE_UNOP(file_sticky) +{ + return (stat->st_mode & S_ISVTX) == 0; +} + +FILE_UNOP(file_linked) +{ + return stat->st_nlink == 0; +} + +FILE_UNOP(file_nonempty) +{ + return stat->st_size == 0; +} + +/* + * Miscellaneous string operators. + */ + +#define STRING_UNOP(name) static int name (const char * str) + +STRING_UNOP(string_empty) +{ + return str[0] != 0; +} + +STRING_UNOP(string_nonempty) +{ + return str[0] == 0; +} + +STRING_UNOP(strfile_istty) +{ + return !isatty(atoi(str)); +} + +STRING_UNOP(strfile_readable) +{ + return access(str, R_OK); +} + +STRING_UNOP(strfile_writeable) +{ + return access(str, W_OK); +} + +STRING_UNOP(strfile_executable) +{ + return access(str, X_OK); +} + +/* + * Declare and fill in some tables of function names and pointers to the + * implementations. + */ + +struct op { + const char *op_name; + void *op_func; +}; +#define OP(name,func) { name, (void *)func } + +const struct op string_binop[] = { + OP("=", cmp_eq), OP("!=", cmp_neq), OP("<", cmp_lt), + OP(">", cmp_gt), OP("<=", cmp_le), OP(">=", cmp_ge) +}; + +const struct op arith_binop[] = { + OP("-eq", cmp_eq), OP("-ne", cmp_neq), OP("-lt", cmp_lt), + OP("-gt", cmp_gt), OP("-le", cmp_le), OP("-ge", cmp_ge) +}; + +const struct op file_binop[] = { + OP("-ef", file_eq), OP("-nt", file_newer), OP("-ot", file_older) +}; + +const struct op string_unop[] = { + OP("-z", string_empty), OP("-n", string_nonempty), + OP("-t", strfile_istty), OP("-r", strfile_readable), + OP("-w", strfile_writeable), OP("-x", strfile_executable) +/* OP ("!", string_empty) */ +}; + +const struct op file_unop[] = { + OP("-b", file_blockspecial), OP("-c", file_charspecial), + OP("-d", file_directory), OP("-p", file_pipe), + OP("-f", file_regular), OP("-g", file_setgid), + OP("-u", file_setuid), OP("-K", file_sticky), + OP("-L", file_linked), OP("-s", file_nonempty), + OP("-e", file_exists) +}; + +#define FIND_OP(table,str) find_op (sizeof (table) / sizeof (* table),\ + table, str) +#define is_string_binop(str) ((compare_p) FIND_OP (string_binop, str)) +#define is_arith_binop(str) ((compare_p) FIND_OP (arith_binop, str)) +#define is_file_binop(str) ((file_binop_p) FIND_OP (file_binop, str)) +#define is_string_unop(str) ((string_unop_p) FIND_OP (string_unop, str)) +#define is_file_unop(str) ((file_unop_p) FIND_OP (file_unop, str)) + +/* + * Generic operator table search routine. + */ + +static void *find_op(int size, const struct op *table, const char *str) +{ + do + if (strcmp(table->op_name, str) == 0) + return table->op_func; + while (table++, --size > 0); + + return NULL; +} + + +/* + * Convert an operand of a numeric expression into a number. The rules for + * how strict the conversion to integer is are not clear. + * + * We return 0 on success, non-zero on failure. + */ + +int convert_number(const char *string, long *numberp) +{ + char *cp; + + *numberp = strtol(string, &cp, 0); + + /* If strtol failed, it leaves cp at start of str. */ + /* Empty arg "" represents 0 for Unix compatability. */ + return (string == cp) ? (*cp != 0) : 0; +} + +enum { + NO_PAREN, + IN_PAREN +}; + +int test_boolor(int argc, char *argv[], int *matchedp, int paren); + + +/* + * Process a non-empty element of a test-expression (see test () below). + * The fundamental premise of this code is that argv [argc - 1] is an + * operand, and thus that argv [argc - 2] is an operator of some form. + */ + +int test_primop(int argc, const char *argv[], int *matchedp, int paren) +{ + int result; + int operator_flag = 1; + + if (argc < 1) { + return SYNTAX_ERROR; /* syntax error in subexpr */ + } + + /* + * Begin by looking for binary operator at argv [argc - 2]. + * String operators only match if we have 3 arguments, and the + * same is true of arithmetic operators. + */ + + if (argc >= 3) { + compare_p cmp; + file_binop_p file; + + *matchedp = 3; + + if ((cmp = is_string_binop(argv[argc - 2])) != NULL) { + result = (*cmp) (strcmp(argv[argc - 3], + argv[argc - 1]), 0); + goto negate; + } + if ((cmp = is_arith_binop(argv[argc - 2])) != NULL) { + long left; + long right; + + if (convert_number(argv[argc - 3], &left) == 0 && + convert_number(argv[argc - 1], &right) == 0) { + + result = (*cmp) (left, right); + goto negate; + } + + return SYNTAX_ERROR; + } + + if ((file = is_file_binop(argv[argc - 2])) != NULL) { + struct stat left; + struct stat right; + + if (stat(argv[argc - 3], &left) < 0 || + stat(argv[argc - 1], &right) < 0) + result = 1; + else + result = (*file) (&left, &right); + goto negate; + } + } + + if (argc >= 2) { + string_unop_p string; + file_unop_p file; + + *matchedp = 2; + + if ((string = is_string_unop(argv[argc - 2])) != NULL) { + result = (*string) (argv[argc - 1]); + goto negate; + } + if ((file = is_file_unop(argv[argc - 2])) != NULL) { + struct stat statbuf; + + if (stat(argv[argc - 1], &statbuf) < 0) + result = 1; + else + result = (*file) (&statbuf); + goto negate; + } + } + + not_really_operator: + operator_flag = 0; + + if (argc > 2 && strcmp(argv[argc - 1], ")") == 0 && + (result = test_boolor(argc - 1, argv, matchedp, + IN_PAREN)) != SYNTAX_ERROR && + argc - 1 > *matchedp && + strcmp(argv[argc - 2 - *matchedp], "(") == 0) + (*matchedp) += 2; + else { + *matchedp = 1; + result = argv[argc - 1][0] == 0; + } + + negate: + /* + * Try and extend the subexpression on the right by looking + * for negations. + */ + + while (argc > *matchedp) { + char *next = argv[argc - 1 - *matchedp]; + + if (strcmp(next, "!") == 0) { + (*matchedp)++; + result = !result; + continue; + } + + /* + * We now have encountered something on the left... if it is + * a conjunction or disjunction operator, we are OK, otherwise + * we have hit a syntax error. If we got here by matching an + * operator, we can try matching a parenthesis and/or string + * as a fallback. + */ + + if (strcmp(next, "-a") != 0 && strcmp(next, "-o") != 0 && + (paren != IN_PAREN || strcmp(next, "(") != 0) && + operator_flag) + goto not_really_operator; + + break; + } + + return result; +} + + +/* + * Process an optional sequence of boolean conjunctions. This is separated + * from disjunction because conjunction has higher "precedence". + */ + +int test_booland(int argc, const char *argv[], int *matchedp, int paren) +{ + int right = 0; /* "true" */ + + *matchedp = 0; + + /* + * Match a sequence of the form: + * and_expr = and_expr AND prim_expr + * | prim_expr ; + * Note that this grammar matches the definition of "and" as left- + * associative, but in fact since conjunction is commutative there is + * no great magic to this and we right-associate instead. + */ + + for (;;) { + int left; + int matched; + + if ((left = test_primop(argc, argv, &matched, + paren)) == SYNTAX_ERROR) { + return SYNTAX_ERROR; + } + + right = right || left; /* "and" */ + *matchedp += matched; + argc -= matched; + + if (argc < 2 || strcmp(argv[argc - 1], "-a") != 0) + return right; + + (*matchedp)++; + argc--; + } +} + + +/* + * Process an optional sequence of boolean disjunctions. + */ + +int test_boolor(int argc, const char *argv[], int *matchedp, int paren) +{ + int right = 1; /* "false" */ + + *matchedp = 0; + + /* + * Match an expression of the form: + * or_expr = or_expr OR and_expr + * | and_expr ; + * Note that this grammar matches the definition of "or" as being + * left-associative, but since disjunction is commutative this does + * not really matter and we actually associate to the right. + */ + + for (;;) { + int left; + int matched; + + if ((left = test_booland(argc, argv, &matched, + paren)) == SYNTAX_ERROR) { + return SYNTAX_ERROR; + } + + (*matchedp) += matched; + argc -= matched; + right = right && left; /* "or" */ + + if (argc < 2 || strcmp(argv[argc - 1], "-o") != 0) + return right; + + (*matchedp)++; + argc--; + } +} + + +/* + * Process a test-expression. The "argc" argument specifies the number of + * elements in the argument-vector "argv", where each element is a string. + * Note that unlike main (), the first element of argv [] is a real + * argument. + */ + +int test(int argc, char *argv[]) +{ + int matched; + int value; + + /* + * Special cases to allow some simple tests. All these are available + * other ways, but they are historically significant. + */ + + if (argc == 0) + return 1; /* false */ + +#if 0 + if (argc == 1) + return argv[0][0] == 0; /* => -n */ +#endif + + if ((value = test_boolor(argc, argv, &matched, + NO_PAREN)) == SYNTAX_ERROR || + matched != argc) { + return SYNTAX_ERROR; + } + + return value; +} + + +#if ! _SHELL + +#define WRITECONST(s) write (2, s, sizeof (s)) + +void error(char **argv, const char *str) +{ + WRITESTR(*argv); + WRITECONST(" "); + + while (*++argv != NULL) { + WRITESTR(*argv); + WRITECONST(" "); + } + WRITECONST(": "); + WRITESTR(str); + WRITECONST("\n"); + +#if 0 + WRITECONST("Unary primaries:\n" + "\t-b file\t\tfile exists and is a block special file\n" + "\t-c file\t\tfile exists and is a character special file\n" + "\t-d file\t\tfile exists and is a directory\n" + "\t-e file\t\tfile exists\n" + "\t-f file\t\tfile exists and is a regular file\n" + "\t-g file\t\tfile exists and is setgid\n" + "\t-k file\t\tfile exists and has sticky bit set\t(not Posix)\n" + "\t-L file\t\tfile is a link\t\t\t\t(not Posix)\n" + "\t-n string\tstring length is nonzero\n" + "\t-p file\t\tfile exists and is a named pipe (FIFO)\n" + "\t-r file\t\tfile exists and is readable\n" + "\t-s file\t\tfile exists and has nonzero size\n" + "\t-t fd\t\tfd is the file descriptor of a terminal\n" + "\t-u file\t\tfile exists and is setuid\n" + "\t-w file\t\tfile exists and is writable\n" + "\t-x file\t\tfile exists and is executable\n" + "\t-z string\tstring length is zero\n" + "\tstring\t\tstring is not the empty string\n"); + WRITECONST("Binary primaries:\n" + "\ts1 = s2\t\tstrings s1 and s2 are identical\n" + "\ts1 != s2\tstrings s1 and s2 are not identical\n" + "\ts1 < s2\t\tstring s1 is less than s2\t\t(not Posix)\n" + "\ts1 > s2\t\tstring s1 is greater than s2\t\t(not Posix)\n" + "\tfile1 -ef file2\tfile1 and file2 are identical\t\t(not Posix)\n" + "\tn1 -eq n2\tnumbers n1 and n2 are equal\n" + "\tn1 -ge n2\tnumber n1 is greater than or equal to n2\n" + "\tn1 -gt n2\tnumber n1 is greater than n2\n" + "\tn1 -le n2\tnumber n1 is less than or equal to n2\n" + "\tn1 -lt n2\tnumber n1 is less than n2\n" + "\tn1 -ne n2\tnumbers n1 and n2 are not equal\n" + "\tfile1 -nt file2\tfile1 is newer than file2\t\t(not Posix)\n" + "\tfile1 -ot file2\tfile1 is older than file2\t\t(not Posix)\n"); + WRITECONST("Expression grouping:\n" + "\t! exp\t\texp is false\n" + "\texp1 -a exp2\texp1 and exp2 are true\t\t\t(not Posix)\n" + "\texp1 -o exp2\texp1 or exp2 is true\t\t\t(not Posix)\n" + "\t( exp )\t\tparentheses for grouping\t\t(not Posix)\n"); +#endif + + exit(2); +} + + +int main(int argc, char *argv[]) +{ + /* + * Let's splurge and use a variable for arg 0 length. + * A small price to pay for clarity and correctness. + */ + + int arg0len; + + /* + * The following comment added by Hal. Keep it here. + * + * If argv[0] ends with '[', look for matching ']'. + * If we just string match "[", it's hard to debug things like + * /tmp/foo/[ xxx yyy zzz ]. + */ + + arg0len = strlen(argv[0]); + + if (argv[0][arg0len - 1] == '[') { + if (strcmp(argv[argc - 1], "]") != 0) + error(argv, "Missing ']'"); + + /* Discard trailing ']'. */ + + argc--; + } + + if ((argc = test(argc - 1, argv + 1)) == 2) + error(argv, "syntax error"); + + return argc; +} + +#endif /* ! _SHELL */ diff --git a/Applications/MWC/cmd/ttt.c b/Applications/MWC/cmd/ttt.c new file mode 100644 index 00000000..c4fdc9a8 --- /dev/null +++ b/Applications/MWC/cmd/ttt.c @@ -0,0 +1,274 @@ +/* + * ttt -- 3-dimensional tic-tac-toe. + */ + +#include +#include +#include +#include + +#define EMPTY 0 +#define MAN 1 +#define BEAST 5 + +/* + * This is a table of all winning + * combinations. + * I stole it out of Kilobaud, April + * 78. + * You can look there to see how it + * is ordered. + */ +const char w[] = { + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11, + 12, 13, 14, 15, + 0, 4, 8, 12, + 1, 5, 9, 13, + 2, 6, 10, 14, + 3, 7, 11, 15, + 0, 5, 10, 15, + 3, 6, 9, 12, + 16, 17, 18, 19, + 20, 21, 22, 23, + 24, 25, 26, 27, + 28, 29, 30, 31, + 16, 20, 24, 28, + 17, 21, 25, 29, + 18, 22, 26, 30, + 19, 23, 27, 31, + 16, 21, 26, 31, + 19, 22, 25, 28, + 32, 33, 34, 35, + 36, 37, 38, 39, + 40, 41, 42, 43, + 44, 45, 46, 47, + 32, 36, 40, 44, + 33, 37, 41, 45, + 34, 38, 42, 46, + 35, 39, 43, 47, + 32, 37, 42, 47, + 35, 38, 41, 44, + 48, 49, 50, 51, + 52, 53, 54, 55, + 56, 57, 58, 59, + 60, 61, 62, 63, + 48, 52, 56, 60, + 49, 53, 57, 61, + 50, 54, 58, 62, + 51, 55, 59, 63, + 48, 53, 58, 63, + 51, 54, 57, 60, + 0, 16, 32, 48, + 1, 17, 33, 49, + 2, 18, 34, 50, + 3, 19, 35, 51, + 4, 20, 36, 52, + 5, 21, 37, 53, + 6, 22, 38, 54, + 7, 23, 39, 55, + 8, 24, 40, 56, + 9, 25, 41, 57, + 10, 26, 42, 58, + 11, 27, 43, 59, + 13, 29, 45, 61, + 12, 28, 44, 60, + 14, 30, 46, 62, + 15, 31, 47, 63, + 0, 21, 42, 63, + 4, 21, 38, 55, + 8, 25, 42, 59, + 12, 25, 38, 51, + 1, 21, 41, 61, + 13, 25, 37, 49, + 2, 22, 42, 62, + 14, 26, 38, 50, + 3, 22, 41, 60, + 7, 22, 37, 52, + 11, 26, 41, 56, + 15, 26, 37, 48, + 0, 20, 40, 60, + 0, 17, 34, 51, + 3, 18, 33, 48, + 3, 23, 43, 63, + 12, 24, 36, 48, + 12, 29, 46, 63, + 15, 30, 45, 60, + 15, 27, 39, 51 +}; + +char b[64]; +const char sep[] = "----------- ----------- ----------- -----------"; + + +int yes(const char *s) +{ + char b[20]; + + for (;;) { + printf("%s? ", s); + if (fgets(b, 20, stdin) == NULL) + exit(0); + if (b[0] == 'y') + return (1); + if (b[0] == 'n') + return (0); + printf("Answer `yes' or `no'.\n"); + } +} + +void rules(void) +{ + printf("Three dimensional tic-tac-toe is played on a 4x4x4\n"); + printf("board. To win you must get 4 in a row. Your moves\n"); + printf("are specified as a 3 digit number; the first digit\n"); + printf("is the level, the second the row and the third the\n"); + printf("column. Levels and columns go from left to right\n"); + printf("from 0 to 3. Rows go from top to bottom with 0 on\n"); + printf("the top.\n"); +} + +void psq(int s) +{ + int v; + + v = b[s]; + if (v == MAN) + printf("UU"); + else if (v == BEAST) + printf("CC"); + else + printf(" "); +} + +void board(void) +{ + int i, j; + + for (i = 0; i < 4; ++i) { + if (i != 0) + puts(sep); + for (j = 0; j < 64; j += 4) { + psq(i + j); + if (j == 12 || j == 28 || j == 44) + putchar(' '); + else if (j >= 60) + putchar('\n'); + else + putchar('!'); + } + } +} + +void man(void) +{ + int i, j, t; + char buf[20]; + + board(); + for (;;) { + if (gets(buf) == NULL) + exit(0); + i = 16 * (buf[0] - '0') + (buf[1] - '0') + 4 * (buf[2] - + '0'); + if (i >= 0 && i <= 63 && b[i] == EMPTY) + break; + printf("?\n"); + } + b[i] = MAN; + for (i = 0; i < 4 * 76; i += 4) { + t = 0; + for (j = 0; j < 4; ++j) + t += b[w[i + j]]; + if (t == 4 * MAN) { + printf("You win.\n"); + exit(0); + } + } +} + +int weight(int t) +{ + if (t == MAN) + return (1); + if (t == 2 * MAN) + return (4); + if (t == BEAST) + return (1); + if (t == 2 * BEAST) + return (2); + return (0); +} + + +void beast(void) +{ + int i, j, t; + int s, bs, bt, v[76]; + + for (i = 0; i < 4 * 76; i += 4) { + t = 0; + for (j = 0; j < 4; ++j) + t += b[w[i + j]]; + v[i >> 2] = t; + if (t == 3 * BEAST) + break; + } + if (i < 4 * 76) { + for (j = 0; j < 4; ++j) + if (b[w[i + j]] == EMPTY) { + b[w[i + j]] = BEAST; + break; + } + board(); + printf("I win.\n"); + exit(0); + } + bt = 0; + for (s = 0; s < 64; ++s) { + if (b[s] != EMPTY) + continue; + t = 0; + for (i = 0; i < 4 * 76; i += 4) { + for (j = 0; j < 4; ++j) + if (w[i + j] == s) + break; + if (j != 4) { + if (v[i >> 2] == 3 * MAN) { + b[s] = BEAST; + return; + } + t += weight(v[i >> 2]); + } + } + if (t > bt) { + bt = t; + bs = s; + } + } + if (bt != 0) + b[bs] = BEAST; + else { + for (s = 0; s < 64; ++s) + if (b[s] == EMPTY) + break; + if (s == 64) { + printf("Draw.\n"); + exit(0); + } + b[s] = BEAST; + } +} + +int main(int argc, char *argv[]) +{ + if (yes("Print rules")) + rules(); + if (yes("Play first")) + man(); + for (;;) { + beast(); + man(); + } +} -- 2.34.1