diff options
Diffstat (limited to 'src/cmd')
45 files changed, 16585 insertions, 0 deletions
diff --git a/src/cmd/ascii.c b/src/cmd/ascii.c new file mode 100644 index 00000000..ee2cb4ca --- /dev/null +++ b/src/cmd/ascii.c @@ -0,0 +1,181 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +#define MAXBASE 36 + +void usage(void); +void put(int); +void putn(int, int); +void puttext(char *); +void putnum(char *); +int btoi(char *); +int value(int, int); +int isnum(char *); + +char *str[256]={ + "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", + "bs ", "ht ", "nl ", "vt ", "np ", "cr ", "so ", "si ", + "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb", + "can", "em ", "sub", "esc", "fs ", "gs ", "rs ", "us ", + "sp ", " ! ", " \" ", " # ", " $ ", " % ", " & ", " ' ", + " ( ", " ) ", " * ", " + ", " , ", " - ", " . ", " / ", + " 0 ", " 1 ", " 2 ", " 3 ", " 4 ", " 5 ", " 6 ", " 7 ", + " 8 ", " 9 ", " : ", " ; ", " < ", " = ", " > ", " ? ", + " @ ", " A ", " B ", " C ", " D ", " E ", " F ", " G ", + " H ", " I ", " J ", " K ", " L ", " M ", " N ", " O ", + " P ", " Q ", " R ", " S ", " T ", " U ", " V ", " W ", + " X ", " Y ", " Z ", " [ ", " \\ ", " ] ", " ^ ", " _ ", + " ` ", " a ", " b ", " c ", " d ", " e ", " f ", " g ", + " h ", " i ", " j ", " k ", " l ", " m ", " n ", " o ", + " p ", " q ", " r ", " s ", " t ", " u ", " v ", " w ", + " x ", " y ", " z ", " { ", " | ", " } ", " ~ ", "del", + "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", + "x88", "x89", "x8a", "x8b", "x8c", "x8d", "x8e", "x8f", + "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", + "x98", "x99", "x9a", "x9b", "x9c", "x9d", "x9e", "x9f", + "xa0", " ¡ ", " ¢ ", " £ ", " ¤ ", " ¥ ", " ¦ ", " § ", + " ¨ ", " © ", " ª ", " « ", " ¬ ", " ", " ® ", " ¯ ", + " ° ", " ± ", " ² ", " ³ ", " ´ ", " µ ", " ¶ ", " · ", + " ¸ ", " ¹ ", " º ", " » ", " ¼ ", " ½ ", " ¾ ", " ¿ ", + " À ", " Á ", " Â ", " Ã ", " Ä ", " Å ", " Æ ", " Ç ", + " È ", " É ", " Ê ", " Ë ", " Ì ", " Í ", " Î ", " Ï ", + " Ð ", " Ñ ", " Ò ", " Ó ", " Ô ", " Õ ", " Ö ", " × ", + " Ø ", " Ù ", " Ú ", " Û ", " Ü ", " Ý ", " Þ ", " ß ", + " à ", " á ", " â ", " ã ", " ä ", " å ", " æ ", " ç ", + " è ", " é ", " ê ", " ë ", " ì ", " í ", " î ", " ï ", + " ð ", " ñ ", " ò ", " ó ", " ô ", " õ ", " ö ", " ÷ ", + " ø ", " ù ", " ú ", " û ", " ü ", " ý ", " þ ", " ÿ " +}; + +char Ncol[]={ + 0,0,7,5,4,4,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +}; + +int nchars=128; +int base=16; +int ncol; +int text=1; +int strip=0; +Biobuf bin; + +void +main(int argc, char **argv) +{ + int i; + + Binit(&bin, 1, OWRITE); + ARGBEGIN{ + case '8': + nchars=256; break; + case 'x': + base=16; break; + case 'o': + base=8; break; + case 'd': + base=10; break; + case 'b': + base=strtoul(EARGF(usage()), 0, 0); + if(base<2||base>MAXBASE) + usage(); + break; + case 'n': + text=0; break; + case 't': + strip=1; + /* fall through */ + case 'c': + text=2; break; + default: + usage(); + }ARGEND + + ncol=Ncol[base]; + if(argc==0){ + for(i=0;i<nchars;i++){ + put(i); + if((i&7)==7) + Bprint(&bin, "|\n"); + } + }else{ + if(text==1) + text=isnum(argv[0]); + while(argc--) + if(text) + puttext(*argv++); + else + putnum(*argv++); + } + Bputc(&bin, '\n'); + exits(0); +} +void +usage(void) +{ + fprint(2, "usage: %s [-8] [-xod | -b8] [-ncst] [--] [text]\n", argv0); + exits("usage"); +} +void +put(int i) +{ + Bputc(&bin, '|'); + putn(i, ncol); + Bprint(&bin, " %s", str[i]); +} +char dig[]="0123456789abcdefghijklmnopqrstuvwxyz"; +void +putn(int n, int ndig) +{ + if(ndig==0) + return; + putn(n/base, ndig-1); + Bputc(&bin, dig[n%base]); +} +void +puttext(char *s) +{ + int n; + n=btoi(s)&0377; + if(strip) + Bputc(&bin, n); + else + Bprint(&bin, "%s\n", str[n]); +} +void +putnum(char *s) +{ + while(*s){ + putn(*s++&0377, ncol); + Bputc(&bin, '\n'); + } +} +int +btoi(char *s) +{ + int n; + n=0; + while(*s) + n=n*base+value(*s++, 0); + return(n); +} +int +value(int c, int f) +{ + char *s; + for(s=dig; s<dig+base; s++) + if(*s==c) + return(s-dig); + if(f) + return(-1); + fprint(2, "%s: bad input char %c\n", argv0, c); + exits("bad"); + return 0; /* to keep ken happy */ +} +int +isnum(char *s) +{ + while(*s) + if(value(*s++, 1)==-1) + return(0); + return(1); +} diff --git a/src/cmd/basename.c b/src/cmd/basename.c new file mode 100644 index 00000000..7b274b22 --- /dev/null +++ b/src/cmd/basename.c @@ -0,0 +1,41 @@ +#include <u.h> +#include <libc.h> + +void +main(int argc, char *argv[]) +{ + char *pr; + int n, dflag; + + dflag = 0; + if(argc>1 && strcmp(argv[1], "-d") == 0){ + --argc; + ++argv; + dflag = 1; + } + if(argc < 2 || argc > 3){ + fprint(2, "usage: basename [-d] string [suffix]\n"); + exits("usage"); + } + pr = utfrrune(argv[1], '/'); + if(dflag){ + if(pr){ + *pr = 0; + print("%s\n", argv[1]); + exits(0); + } + print(".\n"); + exits(0); + } + if(pr) + pr++; + else + pr = argv[1]; + if(argc==3){ + n = strlen(pr)-strlen(argv[2]); + if(n >= 0 && !strcmp(pr+n, argv[2])) + pr[n] = 0; + } + print("%s\n", pr); + exits(0); +} diff --git a/src/cmd/cal.c b/src/cmd/cal.c new file mode 100644 index 00000000..113069b6 --- /dev/null +++ b/src/cmd/cal.c @@ -0,0 +1,313 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +char dayw[] = +{ + " S M Tu W Th F S" +}; +char *smon[] = +{ + "January", "February", "March", "April", + "May", "June", "July", "August", + "September", "October", "November", "December", +}; +char mon[] = +{ + 0, + 31, 29, 31, 30, + 31, 30, 31, 31, + 30, 31, 30, 31, +}; +char string[432]; +Biobuf bout; + +void main(int argc, char *argv[]); +int number(char *str); +void pstr(char *str, int n); +void cal(int m, int y, char *p, int w); +int jan1(int yr); +int curmo(void); +int curyr(void); + +void +main(int argc, char *argv[]) +{ + int y, i, j, m; + + if(argc > 3) { + fprint(2, "usage: cal [month] [year]\n"); + exits("usage"); + } + Binit(&bout, 1, OWRITE); + +/* + * no arg, print current month + */ + if(argc == 1) { + m = curmo(); + y = curyr(); + goto xshort; + } + +/* + * one arg + * if looks like a month, print month + * else print year + */ + if(argc == 2) { + y = number(argv[1]); + if(y < 0) + y = -y; + if(y >= 1 && y <= 12) { + m = y; + y = curyr(); + goto xshort; + } + goto xlong; + } + +/* + * two arg, month and year + */ + m = number(argv[1]); + if(m < 0) + m = -m; + y = number(argv[2]); + goto xshort; + +/* + * print out just month + */ +xshort: + if(m < 1 || m > 12) + goto badarg; + if(y < 1 || y > 9999) + goto badarg; + Bprint(&bout, " %s %ud\n", smon[m-1], y); + Bprint(&bout, "%s\n", dayw); + cal(m, y, string, 24); + for(i=0; i<6*24; i+=24) + pstr(string+i, 24); + exits(0); + +/* + * print out complete year + */ +xlong: + y = number(argv[1]); + if(y<1 || y>9999) + goto badarg; + Bprint(&bout, "\n\n\n"); + Bprint(&bout, " %ud\n", y); + Bprint(&bout, "\n"); + for(i=0; i<12; i+=3) { + for(j=0; j<6*72; j++) + string[j] = '\0'; + Bprint(&bout, " %.3s", smon[i]); + Bprint(&bout, " %.3s", smon[i+1]); + Bprint(&bout, " %.3s\n", smon[i+2]); + Bprint(&bout, "%s %s %s\n", dayw, dayw, dayw); + cal(i+1, y, string, 72); + cal(i+2, y, string+23, 72); + cal(i+3, y, string+46, 72); + for(j=0; j<6*72; j+=72) + pstr(string+j, 72); + } + Bprint(&bout, "\n\n\n"); + exits(0); + +badarg: + Bprint(&bout, "cal: bad argument\n"); +} + +struct +{ + char* word; + int val; +} dict[] = +{ + "jan", 1, + "january", 1, + "feb", 2, + "february", 2, + "mar", 3, + "march", 3, + "apr", 4, + "april", 4, + "may", 5, + "jun", 6, + "june", 6, + "jul", 7, + "july", 7, + "aug", 8, + "august", 8, + "sep", 9, + "sept", 9, + "september", 9, + "oct", 10, + "october", 10, + "nov", 11, + "november", 11, + "dec", 12, + "december", 12, + 0 +}; + +/* + * convert to a number. + * if its a dictionary word, + * return negative number + */ +int +number(char *str) +{ + int n, c; + char *s; + + for(n=0; s=dict[n].word; n++) + if(strcmp(s, str) == 0) + return -dict[n].val; + n = 0; + s = str; + while(c = *s++) { + if(c<'0' || c>'9') + return 0; + n = n*10 + c-'0'; + } + return n; +} + +void +pstr(char *str, int n) +{ + int i; + char *s; + + s = str; + i = n; + while(i--) + if(*s++ == '\0') + s[-1] = ' '; + i = n+1; + while(i--) + if(*--s != ' ') + break; + s[1] = '\0'; + Bprint(&bout, "%s\n", str); +} + +void +cal(int m, int y, char *p, int w) +{ + int d, i; + char *s; + + s = p; + d = jan1(y); + mon[2] = 29; + mon[9] = 30; + + switch((jan1(y+1)+7-d)%7) { + + /* + * non-leap year + */ + case 1: + mon[2] = 28; + break; + + /* + * 1752 + */ + default: + mon[9] = 19; + break; + + /* + * leap year + */ + case 2: + ; + } + for(i=1; i<m; i++) + d += mon[i]; + d %= 7; + s += 3*d; + for(i=1; i<=mon[m]; i++) { + if(i==3 && mon[m]==19) { + i += 11; + mon[m] += 11; + } + if(i > 9) + *s = i/10+'0'; + s++; + *s++ = i%10+'0'; + s++; + if(++d == 7) { + d = 0; + s = p+w; + p = s; + } + } +} + +/* + * return day of the week + * of jan 1 of given year + */ +int +jan1(int yr) +{ + int y, d; + +/* + * normal gregorian calendar + * one extra day per four years + */ + + y = yr; + d = 4+y+(y+3)/4; + +/* + * julian calendar + * regular gregorian + * less three days per 400 + */ + + if(y > 1800) { + d -= (y-1701)/100; + d += (y-1601)/400; + } + +/* + * great calendar changeover instant + */ + + if(y > 1752) + d += 3; + + return d%7; +} + +/* + * system dependent + * get current month and year + */ +int +curmo(void) +{ + Tm *tm; + + tm = localtime(time(0)); + return tm->mon+1; +} + +int +curyr(void) +{ + Tm *tm; + + tm = localtime(time(0)); + return tm->year+1900; +} diff --git a/src/cmd/calendar.c b/src/cmd/calendar.c new file mode 100644 index 00000000..6202c5a3 --- /dev/null +++ b/src/cmd/calendar.c @@ -0,0 +1,195 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <regexp.h> +#include <ctype.h> + +typedef struct Date Date; +struct Date { + Reprog *p; /* an RE to match this date */ + Date *next; /* pointer to next in list */ +}; + +enum{ + Secondsperday = 24*60*60 +}; + +Biobuf in; +int debug, matchyear; + +Date *dates(Date**, Tm*); +void upper2lower(char*, char*, int); +void *alloc(unsigned int); + +void +main(int argc, char *argv[]) +{ + int i, fd, ahead; + long now; + char *line; + Tm *tm; + Date *first, *last, *d; + char buf[1024]; + + ahead = 0; + ARGBEGIN{ + case 'y': + matchyear = 1; + break; + case 'd': + debug = 1; + break; + case 'p': + ahead = atoi(ARGF()); + break; + default: + fprint(2, "usage: calendar [-y] [-d] [files ...]\n"); + exits("usage"); + }ARGEND; + + /* make a list of dates */ + now = time(0); + tm = localtime(now); + last = nil; + first = dates(&last, tm); + now += Secondsperday; + tm = localtime(now); + dates(&last, tm); + if(tm->wday == 6){ + now += Secondsperday; + tm = localtime(now); + dates(&last, tm); + } + if(tm->wday == 0){ + now += Secondsperday; + tm = localtime(now); + dates(&last, tm); + } + if(ahead){ + now = time(0); + now += ahead * Secondsperday; + tm = localtime(now); + dates(&last, tm); + } + + for(i=0; i<argc || (i==0 && argc==0); i++){ + if(i==0 && argc==0) + snprint(buf, sizeof(buf), + "/usr/%s/lib/calendar", getuser()); + else + strcpy(buf, argv[i]); + fd = open(buf, OREAD); + if(fd<0 || Binit(&in, fd, OREAD)<0){ + fprint(2, "calendar: can't open %s: %r\n", buf); + exits("open"); + } + + /* go through the file */ + while(line = Brdline(&in, '\n')){ + line[Blinelen(&in) - 1] = 0; + upper2lower(buf, line, sizeof buf); + for(d=first; d; d=d->next) + if(regexec(d->p, buf, 0, 0)){ + print("%s\n", line); + break; + } + } + close(fd); + } + exits(""); +} + +char *months[] = +{ + "january", + "february", + "march", + "april", + "may", + "june", + "july", + "august", + "september", + "october", + "november", + "december" +}; + +/* + * Generate two Date structures. First has month followed by day; + * second has day followed by month. Link them into list after + * last, and return the first. + */ +Date* +dates(Date **last, Tm *tm) +{ + Date *first; + Date *nd; + char mo[128], buf[128]; + + if(utflen(months[tm->mon]) > 3) + snprint(mo, sizeof mo, "%3.3s(%s)?", + months[tm->mon], months[tm->mon]+3); + else + snprint(mo, sizeof mo, "%3.3s", months[tm->mon]); + if (matchyear) + snprint(buf, sizeof buf, + "(^| |\t)((%s( |\t)+)|(%d/))%d( |\t|$)(((%d|%d)( |\t|$))|[^0-9]|([0-9]+[^0-9 \t]))", + mo, tm->mon+1, tm->mday, tm->year+1900, tm->year%100); + else + snprint(buf, sizeof buf, + "(^| |\t)((%s( |\t)+)|(%d/))%d( |\t|$)", + mo, tm->mon+1, tm->mday); + if(debug) + print("%s\n", buf); + + first = alloc(sizeof(Date)); + if(*last) + (*last)->next = first; + first->p = regcomp(buf); + + if (matchyear) + snprint(buf, sizeof buf, + "(^| |\t)%d( |\t)+(%s)( |\t|$)(((%d|%d)( |\t|$))|[^0-9]|([0-9]+[^0-9 \t]))", + tm->mday, mo, tm->year+1900, tm->year%100); + else + snprint(buf, sizeof buf, + "(^| |\t)%d( |\t)+(%s)( |\t|$)", + tm->mday, mo); + if(debug) + print("%s\n", buf); + nd = alloc(sizeof(Date)); + nd->p = regcomp(buf); + nd->next = 0; + first->next = nd; + *last = nd; + + return first; +} + +/* + * Copy 'from' to 'to', converting to lower case + */ +void +upper2lower(char *to, char *from, int len) +{ + while(--len>0 && *from!='\0') + *to++ = tolower(*from++); + *to = 0; +} + +/* + * Call malloc and check for errors + */ +void* +alloc(unsigned int n) +{ + void *p; + + p = malloc(n); + if(p == 0){ + fprint(2, "calendar: malloc failed: %r\n"); + exits("malloc"); + } + return p; +} diff --git a/src/cmd/cat.c b/src/cmd/cat.c new file mode 100644 index 00000000..ac588897 --- /dev/null +++ b/src/cmd/cat.c @@ -0,0 +1,36 @@ +#include <u.h> +#include <libc.h> + +void +cat(int f, char *s) +{ + char buf[8192]; + long n; + + while((n=read(f, buf, (long)sizeof buf))>0) + if(write(1, buf, n)!=n) + sysfatal("write error copying %s: %r", s); + if(n < 0) + sysfatal("error reading %s: %r", s); +} + +void +main(int argc, char *argv[]) +{ + int f, i; + + argv0 = "cat"; + if(argc == 1) + cat(0, "<stdin>"); + else for(i=1; i<argc; i++){ + f = open(argv[i], OREAD); + if(f < 0) + sysfatal("can't open %s: %r", argv[i]); + else{ + cat(f, argv[i]); + close(f); + } + } + exits(0); +} + diff --git a/src/cmd/cleanname.c b/src/cmd/cleanname.c new file mode 100644 index 00000000..8fa18466 --- /dev/null +++ b/src/cmd/cleanname.c @@ -0,0 +1,44 @@ +#include <u.h> +#include <libc.h> + +void +main(int argc, char **argv) +{ + char *dir; + char *name; + int i; + + dir = nil; + ARGBEGIN{ + case 'd': + if((dir=ARGF()) == nil) + goto Usage; + break; + default: + goto Usage; + }ARGEND; + + if(argc < 1) { + Usage: + fprint(2, "usage: cleanname [-d pwd] name...\n"); + exits("usage"); + } + + for(i=0; i<argc; i++) { + if(dir == nil || argv[i][0] == '/') { + cleanname(argv[i]); + print("%s\n", argv[i]); + } else { + name = malloc(strlen(argv[i])+1+strlen(dir)+1); + if(name == nil) { + fprint(2, "cleanname: out of memory\n"); + exits("out of memory"); + } + sprint(name, "%s/%s", dir, argv[i]); + cleanname(name); + print("%s\n", name); + free(name); + } + } + exits(0); +} diff --git a/src/cmd/cmp.c b/src/cmd/cmp.c new file mode 100644 index 00000000..4f69e181 --- /dev/null +++ b/src/cmd/cmp.c @@ -0,0 +1,112 @@ +#include <u.h> +#include <libc.h> + +#define BUF 65536 + +int sflag = 0; +int lflag = 0; +int Lflag = 0; + +static void usage(void); + +void +main(int argc, char *argv[]) +{ + int n, i; + uchar *p, *q; + uchar buf1[BUF], buf2[BUF]; + int f1, f2; + vlong nc = 1, o, l = 1; + char *name1, *name2; + uchar *b1s, *b1e, *b2s, *b2e; + + ARGBEGIN{ + case 's': sflag = 1; break; + case 'l': lflag = 1; break; + case 'L': Lflag = 1; break; + default: usage(); + }ARGEND + if(argc < 2) + usage(); + if((f1 = open(name1 = *argv++, OREAD)) == -1){ + if(!sflag) perror(name1); + exits("open"); + } + if((f2 = open(name2 = *argv++, OREAD)) == -1){ + if(!sflag) perror(name2); + exits("open"); + } + if(*argv){ + o = strtoll(*argv++, 0, 0); + if(seek(f1, o, 0) < 0){ + if(!sflag) perror("cmp: seek by offset1"); + exits("seek 1"); + } + } + if(*argv){ + o = strtoll(*argv++, 0, 0); + if(seek(f2, o, 0) < 0){ + if(!sflag) perror("cmp: seek by offset2"); + exits("seek 2"); + } + } + if(*argv) + usage(); + b1s = b1e = buf1; + b2s = b2e = buf2; + for(;;){ + if(b1s >= b1e){ + if(b1s >= &buf1[BUF]) + b1s = buf1; + n = read(f1, b1s, &buf1[BUF] - b1s); + b1e = b1s + n; + } + if(b2s >= b2e){ + if(b2s >= &buf2[BUF]) + b2s = buf2; + n = read(f2, b2s, &buf2[BUF] - b2s); + b2e = b2s + n; + } + n = b2e - b2s; + if(n > b1e - b1s) + n = b1e - b1s; + if(n <= 0) + break; + if(memcmp((void *)b1s, (void *)b2s, n) != 0){ + if(sflag) + exits("differ"); + for(p = b1s, q = b2s, i = 0; i < n; p++, q++, i++) { + if(*p == '\n') + l++; + if(*p != *q){ + if(!lflag){ + print("%s %s differ: char %lld", + name1, name2, nc+i); + print(Lflag?" line %lld\n":"\n", l); + exits("differ"); + } + print("%6lld 0x%.2x 0x%.2x\n", nc+i, *p, *q); + } + } + } + if(Lflag) + for(p = b1s; p < b1e;) + if(*p++ == '\n') + l++; + nc += n; + b1s += n; + b2s += n; + } + if(b1e - b1s == b2e - b2s) + exits((char *)0); + if(!sflag) + print("EOF on %s\n", (b1e - b1s > b2e - b2s)? name2 : name1); + exits("EOF"); +} + +static void +usage(void) +{ + print("Usage: cmp [-lsL] file1 file2 [offset1 [offset2] ]\n"); + exits("usage"); +} diff --git a/src/cmd/comm.c b/src/cmd/comm.c new file mode 100644 index 00000000..8109c59c --- /dev/null +++ b/src/cmd/comm.c @@ -0,0 +1,178 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +#define LB 2048 +int one; +int two; +int three; + +char *ldr[3]; + +Biobuf *ib1; +Biobuf *ib2; +Biobuf *openfil(char*); +int rd(Biobuf*, char*); +void wr(char*, int); +void copy(Biobuf*, char*, int); +int compare(char*, char*); + +void +main(int argc, char *argv[]) +{ + int l; + char lb1[LB],lb2[LB]; + + ldr[0] = ""; + ldr[1] = "\t"; + ldr[2] = "\t\t"; + l = 2; + ARGBEGIN{ + case '1': + if(!one) { + one = 1; + ldr[1][0] = + ldr[2][l--] = '\0'; + } + break; + + case '2': + if(!two) { + two = 1; + ldr[2][l--] = '\0'; + } + break; + + case '3': + three = 1; + break; + + default: + goto Usage; + + }ARGEND + + if(argc < 2) { + Usage: + fprint(2, "usage: comm [-123] file1 file2\n"); + exits("usage"); + } + + ib1 = openfil(argv[0]); + ib2 = openfil(argv[1]); + + + if(rd(ib1,lb1) < 0){ + if(rd(ib2,lb2) < 0) + exits(0); + copy(ib2,lb2,2); + } + if(rd(ib2,lb2) < 0) + copy(ib1,lb1,1); + + for(;;){ + switch(compare(lb1,lb2)) { + case 0: + wr(lb1,3); + if(rd(ib1,lb1) < 0) { + if(rd(ib2,lb2) < 0) + exits(0); + copy(ib2,lb2,2); + } + if(rd(ib2,lb2) < 0) + copy(ib1,lb1,1); + continue; + + case 1: + wr(lb1,1); + if(rd(ib1,lb1) < 0) + copy(ib2,lb2,2); + continue; + + case 2: + wr(lb2,2); + if(rd(ib2,lb2) < 0) + copy(ib1,lb1,1); + continue; + } + } + exits(0); +} + +int +rd(Biobuf *file, char *buf) +{ + int i, c; + + i = 0; + while((c = Bgetc(file)) != Beof) { + *buf = c; + if(c == '\n' || i > LB-2) { + *buf = '\0'; + return 0; + } + i++; + buf++; + } + return -1; +} + +void +wr(char *str, int n) +{ + + switch(n){ + case 1: + if(one) + return; + break; + + case 2: + if(two) + return; + break; + + case 3: + if(three) + return; + } + print("%s%s\n", ldr[n-1],str); +} + +void +copy(Biobuf *ibuf, char *lbuf, int n) +{ + do + wr(lbuf,n); + while(rd(ibuf,lbuf) >= 0); + exits(0); +} + +int +compare(char *a, char *b) +{ + while(*a == *b){ + if(*a == '\0') + return 0; + a++; + b++; + } + if(*a < *b) + return 1; + return 2; +} + +Biobuf* +openfil(char *s) +{ + Biobuf *b; + + if(s[0]=='-' && s[1]==0) + s = "/fd/0"; + b = Bopen(s, OREAD); + if(b) + return b; + fprint(2,"comm: cannot open %s: %r\n",s); + exits("open"); + return 0; /* shut up ken */ +} diff --git a/src/cmd/date.c b/src/cmd/date.c new file mode 100644 index 00000000..fc2ec5af --- /dev/null +++ b/src/cmd/date.c @@ -0,0 +1,30 @@ +#include <u.h> +#include <libc.h> + +int uflg, nflg; + +void +main(int argc, char *argv[]) +{ + ulong now; + + ARGBEGIN{ + case 'n': nflg = 1; break; + case 'u': uflg = 1; break; + default: fprint(2, "usage: date [-un] [seconds]\n"); exits("usage"); + }ARGEND + + if(argc == 1) + now = strtoul(*argv, 0, 0); + else + now = time(0); + + if(nflg) + print("%ld\n", now); + else if(uflg) + print("%s", asctime(gmtime(now))); + else + print("%s", ctime(now)); + + exits(0); +} diff --git a/src/cmd/dc.c b/src/cmd/dc.c new file mode 100644 index 00000000..9fe4560d --- /dev/null +++ b/src/cmd/dc.c @@ -0,0 +1,2300 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +typedef void* pointer; + +#define div dcdiv + +#define FATAL 0 +#define NFATAL 1 +#define BLK sizeof(Blk) +#define PTRSZ sizeof(int*) +#define HEADSZ 1024 +#define STKSZ 100 +#define RDSKSZ 100 +#define TBLSZ 256 +#define ARRAYST 221 +#define MAXIND 2048 +#define NL 1 +#define NG 2 +#define NE 3 +#define length(p) ((p)->wt-(p)->beg) +#define rewind(p) (p)->rd=(p)->beg +#define create(p) (p)->rd = (p)->wt = (p)->beg +#define fsfile(p) (p)->rd = (p)->wt +#define truncate(p) (p)->wt = (p)->rd +#define sfeof(p) (((p)->rd==(p)->wt)?1:0) +#define sfbeg(p) (((p)->rd==(p)->beg)?1:0) +#define sungetc(p,c) *(--(p)->rd)=c +#define sgetc(p) (((p)->rd==(p)->wt)?-1:*(p)->rd++) +#define skipc(p) {if((p)->rd<(p)->wt)(p)->rd++;} +#define slookc(p) (((p)->rd==(p)->wt)?-1:*(p)->rd) +#define sbackc(p) (((p)->rd==(p)->beg)?-1:*(--(p)->rd)) +#define backc(p) {if((p)->rd>(p)->beg) --(p)->rd;} +#define sputc(p,c) {if((p)->wt==(p)->last)more(p);\ + *(p)->wt++ = c; } +#define salterc(p,c) {if((p)->rd==(p)->last)more(p);\ + *(p)->rd++ = c;\ + if((p)->rd>(p)->wt)(p)->wt=(p)->rd;} +#define sunputc(p) (*((p)->rd = --(p)->wt)) +#define sclobber(p) ((p)->rd = --(p)->wt) +#define zero(p) for(pp=(p)->beg;pp<(p)->last;)\ + *pp++='\0' +#define OUTC(x) {Bputc(&bout,x); if(--count == 0){Bprint(&bout,"\\\n"); count=ll;} } +#define TEST2 {if((count -= 2) <=0){Bprint(&bout,"\\\n");count=ll;}} +#define EMPTY if(stkerr != 0){Bprint(&bout,"stack empty\n"); continue; } +#define EMPTYR(x) if(stkerr!=0){pushp(x);Bprint(&bout,"stack empty\n");continue;} +#define EMPTYS if(stkerr != 0){Bprint(&bout,"stack empty\n"); return(1);} +#define EMPTYSR(x) if(stkerr !=0){Bprint(&bout,"stack empty\n");pushp(x);return(1);} +#define error(p) {Bprint(&bout,p); continue; } +#define errorrt(p) {Bprint(&bout,p); return(1); } +#define LASTFUN 026 + +typedef struct Blk Blk; +struct Blk +{ + char *rd; + char *wt; + char *beg; + char *last; +}; +typedef struct Sym Sym; +struct Sym +{ + Sym *next; + Blk *val; +}; +typedef struct Wblk Wblk; +struct Wblk +{ + Blk **rdw; + Blk **wtw; + Blk **begw; + Blk **lastw; +}; + +Biobuf *curfile, *fsave; +Blk *arg1, *arg2; +uchar savk; +int dbg; +int ifile; +Blk *scalptr, *basptr, *tenptr, *inbas; +Blk *sqtemp, *chptr, *strptr, *divxyz; +Blk *stack[STKSZ]; +Blk **stkptr,**stkbeg; +Blk **stkend; +Blk *hfree; +int stkerr; +int lastchar; +Blk *readstk[RDSKSZ]; +Blk **readptr; +Blk *rem; +int k; +Blk *irem; +int skd,skr; +int neg; +Sym symlst[TBLSZ]; +Sym *stable[TBLSZ]; +Sym *sptr, *sfree; +long rel; +long nbytes; +long all; +long headmor; +long obase; +int fw,fw1,ll; +void (*outdit)(Blk *p, int flg); +int logo; +int logten; +int count; +char *pp; +char *dummy; +long longest, maxsize, active; +int lall, lrel, lcopy, lmore, lbytes; +int inside; +Biobuf bin; +Biobuf bout; + +void main(int argc, char *argv[]); +void commnds(void); +Blk* readin(void); +Blk* div(Blk *ddivd, Blk *ddivr); +int dscale(void); +Blk* removr(Blk *p, int n); +Blk* dcsqrt(Blk *p); +void init(int argc, char *argv[]); +void onintr(void); +void pushp(Blk *p); +Blk* pop(void); +Blk* readin(void); +Blk* add0(Blk *p, int ct); +Blk* mult(Blk *p, Blk *q); +void chsign(Blk *p); +int readc(void); +void unreadc(char c); +void binop(char c); +void dcprint(Blk *hptr); +Blk* dcexp(Blk *base, Blk *ex); +Blk* getdec(Blk *p, int sc); +void tenot(Blk *p, int sc); +void oneot(Blk *p, int sc, char ch); +void hexot(Blk *p, int flg); +void bigot(Blk *p, int flg); +Blk* add(Blk *a1, Blk *a2); +int eqk(void); +Blk* removc(Blk *p, int n); +Blk* scalint(Blk *p); +Blk* scale(Blk *p, int n); +int subt(void); +int command(void); +int cond(char c); +void load(void); +int log2(long n); +Blk* salloc(int size); +Blk* morehd(void); +Blk* copy(Blk *hptr, int size); +void sdump(char *s1, Blk *hptr); +void seekc(Blk *hptr, int n); +void salterwd(Blk *hptr, Blk *n); +void more(Blk *hptr); +void ospace(char *s); +void garbage(char *s); +void release(Blk *p); +Blk* dcgetwd(Blk *p); +void putwd(Blk *p, Blk *c); +Blk* lookwd(Blk *p); +char* nalloc(char *p, unsigned nbytes); +int getstk(void); + +/********debug only**/ +void +tpr(char *cp, Blk *bp) +{ + print("%s-> ", cp); + print("beg: %lx rd: %lx wt: %lx last: %lx\n", bp->beg, bp->rd, + bp->wt, bp->last); + for (cp = bp->beg; cp != bp->wt; cp++) { + print("%d", *cp); + if (cp != bp->wt-1) + print("/"); + } + print("\n"); +} +/************/ + +void +main(int argc, char *argv[]) +{ + Binit(&bin, 0, OREAD); + Binit(&bout, 1, OWRITE); + init(argc,argv); + commnds(); + exits(0); +} + +void +commnds(void) +{ + Blk *p, *q, **ptr, *s, *t; + long l; + Sym *sp; + int sk, sk1, sk2, c, sign, n, d; + + while(1) { + Bflush(&bout); + if(((c = readc())>='0' && c <= '9') || + (c>='A' && c <='F') || c == '.') { + unreadc(c); + p = readin(); + pushp(p); + continue; + } + switch(c) { + case ' ': + case '\n': + case -1: + continue; + case 'Y': + sdump("stk",*stkptr); + Bprint(&bout, "all %ld rel %ld headmor %ld\n",all,rel,headmor); + Bprint(&bout, "nbytes %ld\n",nbytes); + Bprint(&bout, "longest %ld active %ld maxsize %ld\n", longest, + active, maxsize); + Bprint(&bout, "new all %d rel %d copy %d more %d lbytes %d\n", + lall, lrel, lcopy, lmore, lbytes); + lall = lrel = lcopy = lmore = lbytes = 0; + continue; + case '_': + p = readin(); + savk = sunputc(p); + chsign(p); + sputc(p,savk); + pushp(p); + continue; + case '-': + subt(); + continue; + case '+': + if(eqk() != 0) + continue; + binop('+'); + continue; + case '*': + arg1 = pop(); + EMPTY; + arg2 = pop(); + EMPTYR(arg1); + sk1 = sunputc(arg1); + sk2 = sunputc(arg2); + savk = sk1+sk2; + binop('*'); + p = pop(); + if(savk>k && savk>sk1 && savk>sk2) { + sclobber(p); + sk = sk1; + if(sk<sk2) + sk = sk2; + if(sk<k) + sk = k; + p = removc(p,savk-sk); + savk = sk; + sputc(p,savk); + } + pushp(p); + continue; + case '/': + casediv: + if(dscale() != 0) + continue; + binop('/'); + if(irem != 0) + release(irem); + release(rem); + continue; + case '%': + if(dscale() != 0) + continue; + binop('/'); + p = pop(); + release(p); + if(irem == 0) { + sputc(rem,skr+k); + pushp(rem); + continue; + } + p = add0(rem,skd-(skr+k)); + q = add(p,irem); + release(p); + release(irem); + sputc(q,skd); + pushp(q); + continue; + case 'v': + p = pop(); + EMPTY; + savk = sunputc(p); + if(length(p) == 0) { + sputc(p,savk); + pushp(p); + continue; + } + if(sbackc(p)<0) { + error("sqrt of neg number\n"); + } + if(k<savk) + n = savk; + else { + n = k*2-savk; + savk = k; + } + arg1 = add0(p,n); + arg2 = dcsqrt(arg1); + sputc(arg2,savk); + pushp(arg2); + continue; + + case '^': + neg = 0; + arg1 = pop(); + EMPTY; + if(sunputc(arg1) != 0) + error("exp not an integer\n"); + arg2 = pop(); + EMPTYR(arg1); + if(sfbeg(arg1) == 0 && sbackc(arg1)<0) { + neg++; + chsign(arg1); + } + if(length(arg1)>=3) { + error("exp too big\n"); + } + savk = sunputc(arg2); + p = dcexp(arg2,arg1); + release(arg2); + rewind(arg1); + c = sgetc(arg1); + if(c == -1) + c = 0; + else + if(sfeof(arg1) == 0) + c = sgetc(arg1)*100 + c; + d = c*savk; + release(arg1); + /* if(neg == 0) { removed to fix -exp bug*/ + if(k>=savk) + n = k; + else + n = savk; + if(n<d) { + q = removc(p,d-n); + sputc(q,n); + pushp(q); + } else { + sputc(p,d); + pushp(p); + } + /* } else { this is disaster for exp <-127 */ + /* sputc(p,d); */ + /* pushp(p); */ + /* } */ + if(neg == 0) + continue; + p = pop(); + q = salloc(2); + sputc(q,1); + sputc(q,0); + pushp(q); + pushp(p); + goto casediv; + case 'z': + p = salloc(2); + n = stkptr - stkbeg; + if(n >= 100) { + sputc(p,n/100); + n %= 100; + } + sputc(p,n); + sputc(p,0); + pushp(p); + continue; + case 'Z': + p = pop(); + EMPTY; + n = (length(p)-1)<<1; + fsfile(p); + backc(p); + if(sfbeg(p) == 0) { + if((c = sbackc(p))<0) { + n -= 2; + if(sfbeg(p) == 1) + n++; + else { + if((c = sbackc(p)) == 0) + n++; + else + if(c > 90) + n--; + } + } else + if(c < 10) + n--; + } + release(p); + q = salloc(1); + if(n >= 100) { + sputc(q,n%100); + n /= 100; + } + sputc(q,n); + sputc(q,0); + pushp(q); + continue; + case 'i': + p = pop(); + EMPTY; + p = scalint(p); + release(inbas); + inbas = p; + continue; + case 'I': + p = copy(inbas,length(inbas)+1); + sputc(p,0); + pushp(p); + continue; + case 'o': + p = pop(); + EMPTY; + p = scalint(p); + sign = 0; + n = length(p); + q = copy(p,n); + fsfile(q); + l = c = sbackc(q); + if(n != 1) { + if(c<0) { + sign = 1; + chsign(q); + n = length(q); + fsfile(q); + l = c = sbackc(q); + } + if(n != 1) { + while(sfbeg(q) == 0) + l = l*100+sbackc(q); + } + } + logo = log2(l); + obase = l; + release(basptr); + if(sign == 1) + obase = -l; + basptr = p; + outdit = bigot; + if(n == 1 && sign == 0) { + if(c <= 16) { + outdit = hexot; + fw = 1; + fw1 = 0; + ll = 70; + release(q); + continue; + } + } + n = 0; + if(sign == 1) + n++; + p = salloc(1); + sputc(p,-1); + t = add(p,q); + n += length(t)*2; + fsfile(t); + if(sbackc(t)>9) + n++; + release(t); + release(q); + release(p); + fw = n; + fw1 = n-1; + ll = 70; + if(fw>=ll) + continue; + ll = (70/fw)*fw; + continue; + case 'O': + p = copy(basptr,length(basptr)+1); + sputc(p,0); + pushp(p); + continue; + case '[': + n = 0; + p = salloc(0); + for(;;) { + if((c = readc()) == ']') { + if(n == 0) + break; + n--; + } + sputc(p,c); + if(c == '[') + n++; + } + pushp(p); + continue; + case 'k': + p = pop(); + EMPTY; + p = scalint(p); + if(length(p)>1) { + error("scale too big\n"); + } + rewind(p); + k = 0; + if(!sfeof(p)) + k = sgetc(p); + release(scalptr); + scalptr = p; + continue; + case 'K': + p = copy(scalptr,length(scalptr)+1); + sputc(p,0); + pushp(p); + continue; + case 'X': + p = pop(); + EMPTY; + fsfile(p); + n = sbackc(p); + release(p); + p = salloc(2); + sputc(p,n); + sputc(p,0); + pushp(p); + continue; + case 'Q': + p = pop(); + EMPTY; + if(length(p)>2) { + error("Q?\n"); + } + rewind(p); + if((c = sgetc(p))<0) { + error("neg Q\n"); + } + release(p); + while(c-- > 0) { + if(readptr == &readstk[0]) { + error("readstk?\n"); + } + if(*readptr != 0) + release(*readptr); + readptr--; + } + continue; + case 'q': + if(readptr <= &readstk[1]) + exits(0); + if(*readptr != 0) + release(*readptr); + readptr--; + if(*readptr != 0) + release(*readptr); + readptr--; + continue; + case 'f': + if(stkptr == &stack[0]) + Bprint(&bout,"empty stack\n"); + else { + for(ptr = stkptr; ptr > &stack[0];) { + dcprint(*ptr--); + } + } + continue; + case 'p': + if(stkptr == &stack[0]) + Bprint(&bout,"empty stack\n"); + else { + dcprint(*stkptr); + } + continue; + case 'P': + p = pop(); + EMPTY; + sputc(p,0); + Bprint(&bout,"%s",p->beg); + release(p); + continue; + case 'd': + if(stkptr == &stack[0]) { + Bprint(&bout,"empty stack\n"); + continue; + } + q = *stkptr; + n = length(q); + p = copy(*stkptr,n); + pushp(p); + continue; + case 'c': + while(stkerr == 0) { + p = pop(); + if(stkerr == 0) + release(p); + } + continue; + case 'S': + if(stkptr == &stack[0]) { + error("save: args\n"); + } + c = getstk() & 0377; + sptr = stable[c]; + sp = stable[c] = sfree; + sfree = sfree->next; + if(sfree == 0) + goto sempty; + sp->next = sptr; + p = pop(); + EMPTY; + if(c >= ARRAYST) { + q = copy(p,length(p)+PTRSZ); + for(n = 0;n < PTRSZ;n++) { + sputc(q,0); + } + release(p); + p = q; + } + sp->val = p; + continue; + sempty: + error("symbol table overflow\n"); + case 's': + if(stkptr == &stack[0]) { + error("save:args\n"); + } + c = getstk() & 0377; + sptr = stable[c]; + if(sptr != 0) { + p = sptr->val; + if(c >= ARRAYST) { + rewind(p); + while(sfeof(p) == 0) + release(dcgetwd(p)); + } + release(p); + } else { + sptr = stable[c] = sfree; + sfree = sfree->next; + if(sfree == 0) + goto sempty; + sptr->next = 0; + } + p = pop(); + sptr->val = p; + continue; + case 'l': + load(); + continue; + case 'L': + c = getstk() & 0377; + sptr = stable[c]; + if(sptr == 0) { + error("L?\n"); + } + stable[c] = sptr->next; + sptr->next = sfree; + sfree = sptr; + p = sptr->val; + if(c >= ARRAYST) { + rewind(p); + while(sfeof(p) == 0) { + q = dcgetwd(p); + if(q != 0) + release(q); + } + } + pushp(p); + continue; + case ':': + p = pop(); + EMPTY; + q = scalint(p); + fsfile(q); + c = 0; + if((sfbeg(q) == 0) && ((c = sbackc(q))<0)) { + error("neg index\n"); + } + if(length(q)>2) { + error("index too big\n"); + } + if(sfbeg(q) == 0) + c = c*100+sbackc(q); + if(c >= MAXIND) { + error("index too big\n"); + } + release(q); + n = getstk() & 0377; + sptr = stable[n]; + if(sptr == 0) { + sptr = stable[n] = sfree; + sfree = sfree->next; + if(sfree == 0) + goto sempty; + sptr->next = 0; + p = salloc((c+PTRSZ)*PTRSZ); + zero(p); + } else { + p = sptr->val; + if(length(p)-PTRSZ < c*PTRSZ) { + q = copy(p,(c+PTRSZ)*PTRSZ); + release(p); + p = q; + } + } + seekc(p,c*PTRSZ); + q = lookwd(p); + if(q!=0) + release(q); + s = pop(); + EMPTY; + salterwd(p, s); + sptr->val = p; + continue; + case ';': + p = pop(); + EMPTY; + q = scalint(p); + fsfile(q); + c = 0; + if((sfbeg(q) == 0) && ((c = sbackc(q))<0)) { + error("neg index\n"); + } + if(length(q)>2) { + error("index too big\n"); + } + if(sfbeg(q) == 0) + c = c*100+sbackc(q); + if(c >= MAXIND) { + error("index too big\n"); + } + release(q); + n = getstk() & 0377; + sptr = stable[n]; + if(sptr != 0){ + p = sptr->val; + if(length(p)-PTRSZ >= c*PTRSZ) { + seekc(p,c*PTRSZ); + s = dcgetwd(p); + if(s != 0) { + q = copy(s,length(s)); + pushp(q); + continue; + } + } + } + q = salloc(1); /*so uninitialized array elt prints as 0*/ + sputc(q, 0); + pushp(q); + continue; + case 'x': + execute: + p = pop(); + EMPTY; + if((readptr != &readstk[0]) && (*readptr != 0)) { + if((*readptr)->rd == (*readptr)->wt) + release(*readptr); + else { + if(readptr++ == &readstk[RDSKSZ]) { + error("nesting depth\n"); + } + } + } else + readptr++; + *readptr = p; + if(p != 0) + rewind(p); + else { + if((c = readc()) != '\n') + unreadc(c); + } + continue; + case '?': + if(++readptr == &readstk[RDSKSZ]) { + error("nesting depth\n"); + } + *readptr = 0; + fsave = curfile; + curfile = &bin; + while((c = readc()) == '!') + command(); + p = salloc(0); + sputc(p,c); + while((c = readc()) != '\n') { + sputc(p,c); + if(c == '\\') + sputc(p,readc()); + } + curfile = fsave; + *readptr = p; + continue; + case '!': + if(command() == 1) + goto execute; + continue; + case '<': + case '>': + case '=': + if(cond(c) == 1) + goto execute; + continue; + default: + Bprint(&bout,"%o is unimplemented\n",c); + } + } +} + +Blk* +div(Blk *ddivd, Blk *ddivr) +{ + int divsign, remsign, offset, divcarry, + carry, dig, magic, d, dd, under, first; + long c, td, cc; + Blk *ps, *px, *p, *divd, *divr; + + dig = 0; + under = 0; + divcarry = 0; + rem = 0; + p = salloc(0); + if(length(ddivr) == 0) { + pushp(ddivr); + Bprint(&bout,"divide by 0\n"); + return(p); + } + divsign = remsign = first = 0; + divr = ddivr; + fsfile(divr); + if(sbackc(divr) == -1) { + divr = copy(ddivr,length(ddivr)); + chsign(divr); + divsign = ~divsign; + } + divd = copy(ddivd,length(ddivd)); + fsfile(divd); + if(sfbeg(divd) == 0 && sbackc(divd) == -1) { + chsign(divd); + divsign = ~divsign; + remsign = ~remsign; + } + offset = length(divd) - length(divr); + if(offset < 0) + goto ddone; + seekc(p,offset+1); + sputc(divd,0); + magic = 0; + fsfile(divr); + c = sbackc(divr); + if(c < 10) + magic++; + c = c * 100 + (sfbeg(divr)?0:sbackc(divr)); + if(magic>0){ + c = (c * 100 +(sfbeg(divr)?0:sbackc(divr)))*2; + c /= 25; + } + while(offset >= 0) { + first++; + fsfile(divd); + td = sbackc(divd) * 100; + dd = sfbeg(divd)?0:sbackc(divd); + td = (td + dd) * 100; + dd = sfbeg(divd)?0:sbackc(divd); + td = td + dd; + cc = c; + if(offset == 0) + td++; + else + cc++; + if(magic != 0) + td = td<<3; + dig = td/cc; + under=0; + if(td%cc < 8 && dig > 0 && magic) { + dig--; + under=1; + } + rewind(divr); + rewind(divxyz); + carry = 0; + while(sfeof(divr) == 0) { + d = sgetc(divr)*dig+carry; + carry = d / 100; + salterc(divxyz,d%100); + } + salterc(divxyz,carry); + rewind(divxyz); + seekc(divd,offset); + carry = 0; + while(sfeof(divd) == 0) { + d = slookc(divd); + d = d-(sfeof(divxyz)?0:sgetc(divxyz))-carry; + carry = 0; + if(d < 0) { + d += 100; + carry = 1; + } + salterc(divd,d); + } + divcarry = carry; + backc(p); + salterc(p,dig); + backc(p); + fsfile(divd); + d=sbackc(divd); + if((d != 0) && /*!divcarry*/ (offset != 0)) { + d = sbackc(divd) + 100; + salterc(divd,d); + } + if(--offset >= 0) + divd->wt--; + } + if(under) { /* undershot last - adjust*/ + px = copy(divr,length(divr)); /*11/88 don't corrupt ddivr*/ + chsign(px); + ps = add(px,divd); + fsfile(ps); + if(length(ps) > 0 && sbackc(ps) < 0) { + release(ps); /*only adjust in really undershot*/ + } else { + release(divd); + salterc(p, dig+1); + divd=ps; + } + } + if(divcarry != 0) { + salterc(p,dig-1); + salterc(divd,-1); + ps = add(divr,divd); + release(divd); + divd = ps; + } + + rewind(p); + divcarry = 0; + while(sfeof(p) == 0){ + d = slookc(p)+divcarry; + divcarry = 0; + if(d >= 100){ + d -= 100; + divcarry = 1; + } + salterc(p,d); + } + if(divcarry != 0)salterc(p,divcarry); + fsfile(p); + while(sfbeg(p) == 0) { + if(sbackc(p) != 0) + break; + truncate(p); + } + if(divsign < 0) + chsign(p); + fsfile(divd); + while(sfbeg(divd) == 0) { + if(sbackc(divd) != 0) + break; + truncate(divd); + } +ddone: + if(remsign<0) + chsign(divd); + if(divr != ddivr) + release(divr); + rem = divd; + return(p); +} + +int +dscale(void) +{ + Blk *dd, *dr, *r; + int c; + + dr = pop(); + EMPTYS; + dd = pop(); + EMPTYSR(dr); + fsfile(dd); + skd = sunputc(dd); + fsfile(dr); + skr = sunputc(dr); + if(sfbeg(dr) == 1 || (sfbeg(dr) == 0 && sbackc(dr) == 0)) { + sputc(dr,skr); + pushp(dr); + Bprint(&bout,"divide by 0\n"); + return(1); + } + if(sfbeg(dd) == 1 || (sfbeg(dd) == 0 && sbackc(dd) == 0)) { + sputc(dd,skd); + pushp(dd); + return(1); + } + c = k-skd+skr; + if(c < 0) + r = removr(dd,-c); + else { + r = add0(dd,c); + irem = 0; + } + arg1 = r; + arg2 = dr; + savk = k; + return(0); +} + +Blk* +removr(Blk *p, int n) +{ + int nn, neg; + Blk *q, *s, *r; + + fsfile(p); + neg = sbackc(p); + if(neg < 0) + chsign(p); + rewind(p); + nn = (n+1)/2; + q = salloc(nn); + while(n>1) { + sputc(q,sgetc(p)); + n -= 2; + } + r = salloc(2); + while(sfeof(p) == 0) + sputc(r,sgetc(p)); + release(p); + if(n == 1){ + s = div(r,tenptr); + release(r); + rewind(rem); + if(sfeof(rem) == 0) + sputc(q,sgetc(rem)); + release(rem); + if(neg < 0){ + chsign(s); + chsign(q); + irem = q; + return(s); + } + irem = q; + return(s); + } + if(neg < 0) { + chsign(r); + chsign(q); + irem = q; + return(r); + } + irem = q; + return(r); +} + +Blk* +dcsqrt(Blk *p) +{ + Blk *t, *r, *q, *s; + int c, n, nn; + + n = length(p); + fsfile(p); + c = sbackc(p); + if((n&1) != 1) + c = c*100+(sfbeg(p)?0:sbackc(p)); + n = (n+1)>>1; + r = salloc(n); + zero(r); + seekc(r,n); + nn=1; + while((c -= nn)>=0) + nn+=2; + c=(nn+1)>>1; + fsfile(r); + backc(r); + if(c>=100) { + c -= 100; + salterc(r,c); + sputc(r,1); + } else + salterc(r,c); + for(;;){ + q = div(p,r); + s = add(q,r); + release(q); + release(rem); + q = div(s,sqtemp); + release(s); + release(rem); + s = copy(r,length(r)); + chsign(s); + t = add(s,q); + release(s); + fsfile(t); + nn = sfbeg(t)?0:sbackc(t); + if(nn>=0) + break; + release(r); + release(t); + r = q; + } + release(t); + release(q); + release(p); + return(r); +} + +Blk* +dcexp(Blk *base, Blk *ex) +{ + Blk *r, *e, *p, *e1, *t, *cp; + int temp, c, n; + + r = salloc(1); + sputc(r,1); + p = copy(base,length(base)); + e = copy(ex,length(ex)); + fsfile(e); + if(sfbeg(e) != 0) + goto edone; + temp=0; + c = sbackc(e); + if(c<0) { + temp++; + chsign(e); + } + while(length(e) != 0) { + e1=div(e,sqtemp); + release(e); + e = e1; + n = length(rem); + release(rem); + if(n != 0) { + e1=mult(p,r); + release(r); + r = e1; + } + t = copy(p,length(p)); + cp = mult(p,t); + release(p); + release(t); + p = cp; + } + if(temp != 0) { + if((c = length(base)) == 0) { + goto edone; + } + if(c>1) + create(r); + else { + rewind(base); + if((c = sgetc(base))<=1) { + create(r); + sputc(r,c); + } else + create(r); + } + } +edone: + release(p); + release(e); + return(r); +} + +void +init(int argc, char *argv[]) +{ + Sym *sp; + Dir *d; + + ARGBEGIN { + default: + dbg = 1; + break; + } ARGEND + ifile = 1; + curfile = &bin; + if(*argv){ + d = dirstat(*argv); + if(d == nil) { + fprint(2, "dc: can't open file %s\n", *argv); + exits("open"); + } + if(d->mode & DMDIR) { + fprint(2, "dc: file %s is a directory\n", *argv); + exits("open"); + } + free(d); + if((curfile = Bopen(*argv, OREAD)) == 0) { + fprint(2,"dc: can't open file %s\n", *argv); + exits("open"); + } + } +/* dummy = malloc(0); *//* prepare for garbage-collection */ + scalptr = salloc(1); + sputc(scalptr,0); + basptr = salloc(1); + sputc(basptr,10); + obase=10; + logten=log2(10L); + ll=70; + fw=1; + fw1=0; + tenptr = salloc(1); + sputc(tenptr,10); + obase=10; + inbas = salloc(1); + sputc(inbas,10); + sqtemp = salloc(1); + sputc(sqtemp,2); + chptr = salloc(0); + strptr = salloc(0); + divxyz = salloc(0); + stkbeg = stkptr = &stack[0]; + stkend = &stack[STKSZ]; + stkerr = 0; + readptr = &readstk[0]; + k=0; + sp = sptr = &symlst[0]; + while(sptr < &symlst[TBLSZ]) { + sptr->next = ++sp; + sptr++; + } + sptr->next=0; + sfree = &symlst[0]; +} + +void +pushp(Blk *p) +{ + if(stkptr == stkend) { + Bprint(&bout,"out of stack space\n"); + return; + } + stkerr=0; + *++stkptr = p; + return; +} + +Blk* +pop(void) +{ + if(stkptr == stack) { + stkerr=1; + return(0); + } + return(*stkptr--); +} + +Blk* +readin(void) +{ + Blk *p, *q; + int dp, dpct, c; + + dp = dpct=0; + p = salloc(0); + for(;;){ + c = readc(); + switch(c) { + case '.': + if(dp != 0) + goto gotnum; + dp++; + continue; + case '\\': + readc(); + continue; + default: + if(c >= 'A' && c <= 'F') + c = c - 'A' + 10; + else + if(c >= '0' && c <= '9') + c -= '0'; + else + goto gotnum; + if(dp != 0) { + if(dpct >= 99) + continue; + dpct++; + } + create(chptr); + if(c != 0) + sputc(chptr,c); + q = mult(p,inbas); + release(p); + p = add(chptr,q); + release(q); + } + } +gotnum: + unreadc(c); + if(dp == 0) { + sputc(p,0); + return(p); + } else { + q = scale(p,dpct); + return(q); + } +} + +/* + * returns pointer to struct with ct 0's & p + */ +Blk* +add0(Blk *p, int ct) +{ + Blk *q, *t; + + q = salloc(length(p)+(ct+1)/2); + while(ct>1) { + sputc(q,0); + ct -= 2; + } + rewind(p); + while(sfeof(p) == 0) { + sputc(q,sgetc(p)); + } + release(p); + if(ct == 1) { + t = mult(tenptr,q); + release(q); + return(t); + } + return(q); +} + +Blk* +mult(Blk *p, Blk *q) +{ + Blk *mp, *mq, *mr; + int sign, offset, carry; + int cq, cp, mt, mcr; + + offset = sign = 0; + fsfile(p); + mp = p; + if(sfbeg(p) == 0) { + if(sbackc(p)<0) { + mp = copy(p,length(p)); + chsign(mp); + sign = ~sign; + } + } + fsfile(q); + mq = q; + if(sfbeg(q) == 0){ + if(sbackc(q)<0) { + mq = copy(q,length(q)); + chsign(mq); + sign = ~sign; + } + } + mr = salloc(length(mp)+length(mq)); + zero(mr); + rewind(mq); + while(sfeof(mq) == 0) { + cq = sgetc(mq); + rewind(mp); + rewind(mr); + mr->rd += offset; + carry=0; + while(sfeof(mp) == 0) { + cp = sgetc(mp); + mcr = sfeof(mr)?0:slookc(mr); + mt = cp*cq + carry + mcr; + carry = mt/100; + salterc(mr,mt%100); + } + offset++; + if(carry != 0) { + mcr = sfeof(mr)?0:slookc(mr); + salterc(mr,mcr+carry); + } + } + if(sign < 0) { + chsign(mr); + } + if(mp != p) + release(mp); + if(mq != q) + release(mq); + return(mr); +} + +void +chsign(Blk *p) +{ + int carry; + char ct; + + carry=0; + rewind(p); + while(sfeof(p) == 0) { + ct=100-slookc(p)-carry; + carry=1; + if(ct>=100) { + ct -= 100; + carry=0; + } + salterc(p,ct); + } + if(carry != 0) { + sputc(p,-1); + fsfile(p); + backc(p); + ct = sbackc(p); + if(ct == 99 /*&& !sfbeg(p)*/) { + truncate(p); + sputc(p,-1); + } + } else{ + fsfile(p); + ct = sbackc(p); + if(ct == 0) + truncate(p); + } + return; +} + +int +readc(void) +{ +loop: + if((readptr != &readstk[0]) && (*readptr != 0)) { + if(sfeof(*readptr) == 0) + return(lastchar = sgetc(*readptr)); + release(*readptr); + readptr--; + goto loop; + } + lastchar = Bgetc(curfile); + if(lastchar != -1) + return(lastchar); + if(readptr != &readptr[0]) { + readptr--; + if(*readptr == 0) + curfile = &bin; + goto loop; + } + if(curfile != &bin) { + Bterm(curfile); + curfile = &bin; + goto loop; + } + exits(0); + return 0; /* shut up ken */ +} + +void +unreadc(char c) +{ + + if((readptr != &readstk[0]) && (*readptr != 0)) { + sungetc(*readptr,c); + } else + Bungetc(curfile); + return; +} + +void +binop(char c) +{ + Blk *r; + + r = 0; + switch(c) { + case '+': + r = add(arg1,arg2); + break; + case '*': + r = mult(arg1,arg2); + break; + case '/': + r = div(arg1,arg2); + break; + } + release(arg1); + release(arg2); + sputc(r,savk); + pushp(r); +} + +void +dcprint(Blk *hptr) +{ + Blk *p, *q, *dec; + int dig, dout, ct, sc; + + rewind(hptr); + while(sfeof(hptr) == 0) { + if(sgetc(hptr)>99) { + rewind(hptr); + while(sfeof(hptr) == 0) { + Bprint(&bout,"%c",sgetc(hptr)); + } + Bprint(&bout,"\n"); + return; + } + } + fsfile(hptr); + sc = sbackc(hptr); + if(sfbeg(hptr) != 0) { + Bprint(&bout,"0\n"); + return; + } + count = ll; + p = copy(hptr,length(hptr)); + sclobber(p); + fsfile(p); + if(sbackc(p)<0) { + chsign(p); + OUTC('-'); + } + if((obase == 0) || (obase == -1)) { + oneot(p,sc,'d'); + return; + } + if(obase == 1) { + oneot(p,sc,'1'); + return; + } + if(obase == 10) { + tenot(p,sc); + return; + } + /* sleazy hack to scale top of stack - divide by 1 */ + pushp(p); + sputc(p, sc); + p=salloc(0); + create(p); + sputc(p, 1); + sputc(p, 0); + pushp(p); + if(dscale() != 0) + return; + p = div(arg1, arg2); + release(arg1); + release(arg2); + sc = savk; + + create(strptr); + dig = logten*sc; + dout = ((dig/10) + dig) / logo; + dec = getdec(p,sc); + p = removc(p,sc); + while(length(p) != 0) { + q = div(p,basptr); + release(p); + p = q; + (*outdit)(rem,0); + } + release(p); + fsfile(strptr); + while(sfbeg(strptr) == 0) + OUTC(sbackc(strptr)); + if(sc == 0) { + release(dec); + Bprint(&bout,"\n"); + return; + } + create(strptr); + OUTC('.'); + ct=0; + do { + q = mult(basptr,dec); + release(dec); + dec = getdec(q,sc); + p = removc(q,sc); + (*outdit)(p,1); + } while(++ct < dout); + release(dec); + rewind(strptr); + while(sfeof(strptr) == 0) + OUTC(sgetc(strptr)); + Bprint(&bout,"\n"); +} + +Blk* +getdec(Blk *p, int sc) +{ + int cc; + Blk *q, *t, *s; + + rewind(p); + if(length(p)*2 < sc) { + q = copy(p,length(p)); + return(q); + } + q = salloc(length(p)); + while(sc >= 1) { + sputc(q,sgetc(p)); + sc -= 2; + } + if(sc != 0) { + t = mult(q,tenptr); + s = salloc(cc = length(q)); + release(q); + rewind(t); + while(cc-- > 0) + sputc(s,sgetc(t)); + sputc(s,0); + release(t); + t = div(s,tenptr); + release(s); + release(rem); + return(t); + } + return(q); +} + +void +tenot(Blk *p, int sc) +{ + int c, f; + + fsfile(p); + f=0; + while((sfbeg(p) == 0) && ((p->rd-p->beg-1)*2 >= sc)) { + c = sbackc(p); + if((c<10) && (f == 1)) + Bprint(&bout,"0%d",c); + else + Bprint(&bout,"%d",c); + f=1; + TEST2; + } + if(sc == 0) { + Bprint(&bout,"\n"); + release(p); + return; + } + if((p->rd-p->beg)*2 > sc) { + c = sbackc(p); + Bprint(&bout,"%d.",c/10); + TEST2; + OUTC(c%10 +'0'); + sc--; + } else { + OUTC('.'); + } + while(sc>(p->rd-p->beg)*2) { + OUTC('0'); + sc--; + } + while(sc > 1) { + c = sbackc(p); + if(c<10) + Bprint(&bout,"0%d",c); + else + Bprint(&bout,"%d",c); + sc -= 2; + TEST2; + } + if(sc == 1) { + OUTC(sbackc(p)/10 +'0'); + } + Bprint(&bout,"\n"); + release(p); +} + +void +oneot(Blk *p, int sc, char ch) +{ + Blk *q; + + q = removc(p,sc); + create(strptr); + sputc(strptr,-1); + while(length(q)>0) { + p = add(strptr,q); + release(q); + q = p; + OUTC(ch); + } + release(q); + Bprint(&bout,"\n"); +} + +void +hexot(Blk *p, int flg) +{ + int c; + + USED(flg); + rewind(p); + if(sfeof(p) != 0) { + sputc(strptr,'0'); + release(p); + return; + } + c = sgetc(p); + release(p); + if(c >= 16) { + Bprint(&bout,"hex digit > 16"); + return; + } + sputc(strptr,c<10?c+'0':c-10+'a'); +} + +void +bigot(Blk *p, int flg) +{ + Blk *t, *q; + int neg, l; + + if(flg == 1) { + t = salloc(0); + l = 0; + } else { + t = strptr; + l = length(strptr)+fw-1; + } + neg=0; + if(length(p) != 0) { + fsfile(p); + if(sbackc(p)<0) { + neg=1; + chsign(p); + } + while(length(p) != 0) { + q = div(p,tenptr); + release(p); + p = q; + rewind(rem); + sputc(t,sfeof(rem)?'0':sgetc(rem)+'0'); + release(rem); + } + } + release(p); + if(flg == 1) { + l = fw1-length(t); + if(neg != 0) { + l--; + sputc(strptr,'-'); + } + fsfile(t); + while(l-- > 0) + sputc(strptr,'0'); + while(sfbeg(t) == 0) + sputc(strptr,sbackc(t)); + release(t); + } else { + l -= length(strptr); + while(l-- > 0) + sputc(strptr,'0'); + if(neg != 0) { + sclobber(strptr); + sputc(strptr,'-'); + } + } + sputc(strptr,' '); +} + +Blk* +add(Blk *a1, Blk *a2) +{ + Blk *p; + int carry, n, size, c, n1, n2; + + size = length(a1)>length(a2)?length(a1):length(a2); + p = salloc(size); + rewind(a1); + rewind(a2); + carry=0; + while(--size >= 0) { + n1 = sfeof(a1)?0:sgetc(a1); + n2 = sfeof(a2)?0:sgetc(a2); + n = n1 + n2 + carry; + if(n>=100) { + carry=1; + n -= 100; + } else + if(n<0) { + carry = -1; + n += 100; + } else + carry = 0; + sputc(p,n); + } + if(carry != 0) + sputc(p,carry); + fsfile(p); + if(sfbeg(p) == 0) { + c = 0; + while(sfbeg(p) == 0 && (c = sbackc(p)) == 0) + ; + if(c != 0) + salterc(p,c); + truncate(p); + } + fsfile(p); + if(sfbeg(p) == 0 && sbackc(p) == -1) { + while((c = sbackc(p)) == 99) { + if(c == -1) + break; + } + skipc(p); + salterc(p,-1); + truncate(p); + } + return(p); +} + +int +eqk(void) +{ + Blk *p, *q; + int skp, skq; + + p = pop(); + EMPTYS; + q = pop(); + EMPTYSR(p); + skp = sunputc(p); + skq = sunputc(q); + if(skp == skq) { + arg1=p; + arg2=q; + savk = skp; + return(0); + } + if(skp < skq) { + savk = skq; + p = add0(p,skq-skp); + } else { + savk = skp; + q = add0(q,skp-skq); + } + arg1=p; + arg2=q; + return(0); +} + +Blk* +removc(Blk *p, int n) +{ + Blk *q, *r; + + rewind(p); + while(n>1) { + skipc(p); + n -= 2; + } + q = salloc(2); + while(sfeof(p) == 0) + sputc(q,sgetc(p)); + if(n == 1) { + r = div(q,tenptr); + release(q); + release(rem); + q = r; + } + release(p); + return(q); +} + +Blk* +scalint(Blk *p) +{ + int n; + + n = sunputc(p); + p = removc(p,n); + return(p); +} + +Blk* +scale(Blk *p, int n) +{ + Blk *q, *s, *t; + + t = add0(p,n); + q = salloc(1); + sputc(q,n); + s = dcexp(inbas,q); + release(q); + q = div(t,s); + release(t); + release(s); + release(rem); + sputc(q,n); + return(q); +} + +int +subt(void) +{ + arg1=pop(); + EMPTYS; + savk = sunputc(arg1); + chsign(arg1); + sputc(arg1,savk); + pushp(arg1); + if(eqk() != 0) + return(1); + binop('+'); + return(0); +} + +int +command(void) +{ + char line[100], *sl; + int pid, p, c; + + switch(c = readc()) { + case '<': + return(cond(NL)); + case '>': + return(cond(NG)); + case '=': + return(cond(NE)); + default: + sl = line; + *sl++ = c; + while((c = readc()) != '\n') + *sl++ = c; + *sl = 0; + if((pid = fork()) == 0) { + execl("/bin/rc","rc","-c",line,0); + exits("shell"); + } + for(;;) { + if((p = waitpid()) < 0) + break; + if(p== pid) + break; + } + Bprint(&bout,"!\n"); + return(0); + } +} + +int +cond(char c) +{ + Blk *p; + int cc; + + if(subt() != 0) + return(1); + p = pop(); + sclobber(p); + if(length(p) == 0) { + release(p); + if(c == '<' || c == '>' || c == NE) { + getstk(); + return(0); + } + load(); + return(1); + } + if(c == '='){ + release(p); + getstk(); + return(0); + } + if(c == NE) { + release(p); + load(); + return(1); + } + fsfile(p); + cc = sbackc(p); + release(p); + if((cc<0 && (c == '<' || c == NG)) || + (cc >0) && (c == '>' || c == NL)) { + getstk(); + return(0); + } + load(); + return(1); +} + +void +load(void) +{ + int c; + Blk *p, *q, *t, *s; + + c = getstk() & 0377; + sptr = stable[c]; + if(sptr != 0) { + p = sptr->val; + if(c >= ARRAYST) { + q = salloc(length(p)); + rewind(p); + while(sfeof(p) == 0) { + s = dcgetwd(p); + if(s == 0) { + putwd(q, (Blk*)0); + } else { + t = copy(s,length(s)); + putwd(q,t); + } + } + pushp(q); + } else { + q = copy(p,length(p)); + pushp(q); + } + } else { + q = salloc(1); + if(c <= LASTFUN) { + Bprint(&bout,"function %c undefined\n",c+'a'-1); + sputc(q,'c'); + sputc(q,'0'); + sputc(q,' '); + sputc(q,'1'); + sputc(q,'Q'); + } + else + sputc(q,0); + pushp(q); + } +} + +int +log2(long n) +{ + int i; + + if(n == 0) + return(0); + i=31; + if(n<0) + return(i); + while((n= n<<1) >0) + i--; + return i-1; +} + +Blk* +salloc(int size) +{ + Blk *hdr; + char *ptr; + + all++; + lall++; + if(all - rel > active) + active = all - rel; + nbytes += size; + lbytes += size; + if(nbytes >maxsize) + maxsize = nbytes; + if(size > longest) + longest = size; + ptr = malloc((unsigned)size); + if(ptr == 0){ + garbage("salloc"); + if((ptr = malloc((unsigned)size)) == 0) + ospace("salloc"); + } + if((hdr = hfree) == 0) + hdr = morehd(); + hfree = (Blk *)hdr->rd; + hdr->rd = hdr->wt = hdr->beg = ptr; + hdr->last = ptr+size; + return(hdr); +} + +Blk* +morehd(void) +{ + Blk *h, *kk; + + headmor++; + nbytes += HEADSZ; + hfree = h = (Blk *)malloc(HEADSZ); + if(hfree == 0) { + garbage("morehd"); + if((hfree = h = (Blk*)malloc(HEADSZ)) == 0) + ospace("headers"); + } + kk = h; + while(h<hfree+(HEADSZ/BLK)) + (h++)->rd = (char*)++kk; + (h-1)->rd=0; + return(hfree); +} + +Blk* +copy(Blk *hptr, int size) +{ + Blk *hdr; + unsigned sz; + char *ptr; + + all++; + lall++; + lcopy++; + nbytes += size; + lbytes += size; + if(size > longest) + longest = size; + if(size > maxsize) + maxsize = size; + sz = length(hptr); + ptr = nalloc(hptr->beg, size); + if(ptr == 0) { + garbage("copy"); + if((ptr = nalloc(hptr->beg, size)) == 0) { + Bprint(&bout,"copy size %d\n",size); + ospace("copy"); + } + } + if((hdr = hfree) == 0) + hdr = morehd(); + hfree = (Blk *)hdr->rd; + hdr->rd = hdr->beg = ptr; + hdr->last = ptr+size; + hdr->wt = ptr+sz; + ptr = hdr->wt; + while(ptr<hdr->last) + *ptr++ = '\0'; + return(hdr); +} + +void +sdump(char *s1, Blk *hptr) +{ + char *p; + + Bprint(&bout,"%s %lx rd %lx wt %lx beg %lx last %lx\n", + s1,hptr,hptr->rd,hptr->wt,hptr->beg,hptr->last); + p = hptr->beg; + while(p < hptr->wt) + Bprint(&bout,"%d ",*p++); + Bprint(&bout,"\n"); +} + +void +seekc(Blk *hptr, int n) +{ + char *nn,*p; + + nn = hptr->beg+n; + if(nn > hptr->last) { + nbytes += nn - hptr->last; + if(nbytes > maxsize) + maxsize = nbytes; + lbytes += nn - hptr->last; + if(n > longest) + longest = n; +/* free(hptr->beg); *//**/ + p = realloc(hptr->beg, n); + if(p == 0) { +/* hptr->beg = realloc(hptr->beg, hptr->last-hptr->beg); +** garbage("seekc"); +** if((p = realloc(hptr->beg, n)) == 0) +*/ ospace("seekc"); + } + hptr->beg = p; + hptr->wt = hptr->last = hptr->rd = p+n; + return; + } + hptr->rd = nn; + if(nn>hptr->wt) + hptr->wt = nn; +} + +void +salterwd(Blk *ahptr, Blk *n) +{ + Wblk *hptr; + + hptr = (Wblk*)ahptr; + if(hptr->rdw == hptr->lastw) + more(ahptr); + *hptr->rdw++ = n; + if(hptr->rdw > hptr->wtw) + hptr->wtw = hptr->rdw; +} + +void +more(Blk *hptr) +{ + unsigned size; + char *p; + + if((size=(hptr->last-hptr->beg)*2) == 0) + size=2; + nbytes += size/2; + if(nbytes > maxsize) + maxsize = nbytes; + if(size > longest) + longest = size; + lbytes += size/2; + lmore++; +/* free(hptr->beg);*//**/ + p = realloc(hptr->beg, size); + + if(p == 0) { +/* hptr->beg = realloc(hptr->beg, (hptr->last-hptr->beg)); +** garbage("more"); +** if((p = realloc(hptr->beg,size)) == 0) +*/ ospace("more"); + } + hptr->rd = p + (hptr->rd - hptr->beg); + hptr->wt = p + (hptr->wt - hptr->beg); + hptr->beg = p; + hptr->last = p+size; +} + +void +ospace(char *s) +{ + Bprint(&bout,"out of space: %s\n",s); + Bprint(&bout,"all %ld rel %ld headmor %ld\n",all,rel,headmor); + Bprint(&bout,"nbytes %ld\n",nbytes); + sdump("stk",*stkptr); + abort(); +} + +void +garbage(char *s) +{ + USED(s); +} + +void +release(Blk *p) +{ + rel++; + lrel++; + nbytes -= p->last - p->beg; + p->rd = (char*)hfree; + hfree = p; + free(p->beg); +} + +Blk* +dcgetwd(Blk *p) +{ + Wblk *wp; + + wp = (Wblk*)p; + if(wp->rdw == wp->wtw) + return(0); + return(*wp->rdw++); +} + +void +putwd(Blk *p, Blk *c) +{ + Wblk *wp; + + wp = (Wblk*)p; + if(wp->wtw == wp->lastw) + more(p); + *wp->wtw++ = c; +} + +Blk* +lookwd(Blk *p) +{ + Wblk *wp; + + wp = (Wblk*)p; + if(wp->rdw == wp->wtw) + return(0); + return(*wp->rdw); +} + +char* +nalloc(char *p, unsigned nbytes) +{ + char *q, *r; + + q = r = malloc(nbytes); + if(q==0) + return(0); + while(nbytes--) + *q++ = *p++; + return(r); +} + +int +getstk(void) +{ + int n; + uchar c; + + c = readc(); + if(c != '<') + return c; + n = 0; + while(1) { + c = readc(); + if(c == '>') + break; + n = n*10+c-'0'; + } + return n; +} diff --git a/src/cmd/dd.c b/src/cmd/dd.c new file mode 100644 index 00000000..15599145 --- /dev/null +++ b/src/cmd/dd.c @@ -0,0 +1,660 @@ +#include <u.h> +#include <libc.h> + +#define BIG 2147483647 +#define LCASE (1<<0) +#define UCASE (1<<1) +#define SWAB (1<<2) +#define NERR (1<<3) +#define SYNC (1<<4) +int cflag; +int fflag; +char *string; +char *ifile; +char *ofile; +char *ibuf; +char *obuf; +vlong skip; +vlong oseekn; +vlong iseekn; +vlong count; +long files = 1; +long ibs = 512; +long obs = 512; +long bs; +long cbs; +long ibc; +long obc; +long cbc; +long nifr; +long nipr; +long nofr; +long nopr; +long ntrunc; +int dotrunc = 1; +int ibf; +int obf; +char *op; +int nspace; +uchar etoa[256]; +uchar atoe[256]; +uchar atoibm[256]; + +void flsh(void); +int match(char *s); +vlong number(long big); +void cnull(int cc); +void null(int c); +void ascii(int cc); +void unblock(int cc); +void ebcdic(int cc); +void ibm(int cc); +void block(int cc); +void term(void); +void stats(void); + +#define iskey(s) ((key[0] == '-') && (strcmp(key+1, s) == 0)) + +void +main(int argc, char *argv[]) +{ + void (*conv)(int); + char *ip; + char *key; + int a, c; + + conv = null; + for(c=1; c<argc; c++) { + key = argv[c++]; + if(c >= argc){ + fprint(2, "dd: arg %s needs a value\n", key); + exits("arg"); + } + string = argv[c]; + if(iskey("ibs")) { + ibs = number(BIG); + continue; + } + if(iskey("obs")) { + obs = number(BIG); + continue; + } + if(iskey("cbs")) { + cbs = number(BIG); + continue; + } + if(iskey("bs")) { + bs = number(BIG); + continue; + } + if(iskey("if")) { + ifile = string; + continue; + } + if(iskey("of")) { + ofile = string; + continue; + } + if(iskey("trunc")) { + dotrunc = number(BIG); + continue; + } + if(iskey("skip")) { + skip = number(BIG); + continue; + } + if(iskey("seek") || iskey("oseek")) { + oseekn = number(BIG); + continue; + } + if(iskey("iseek")) { + iseekn = number(BIG); + continue; + } + if(iskey("count")) { + count = number(BIG); + continue; + } + if(iskey("files")) { + files = number(BIG); + continue; + } + if(iskey("conv")) { + cloop: + if(match(",")) + goto cloop; + if(*string == '\0') + continue; + if(match("ebcdic")) { + conv = ebcdic; + goto cloop; + } + if(match("ibm")) { + conv = ibm; + goto cloop; + } + if(match("ascii")) { + conv = ascii; + goto cloop; + } + if(match("block")) { + conv = block; + goto cloop; + } + if(match("unblock")) { + conv = unblock; + goto cloop; + } + if(match("lcase")) { + cflag |= LCASE; + goto cloop; + } + if(match("ucase")) { + cflag |= UCASE; + goto cloop; + } + if(match("swab")) { + cflag |= SWAB; + goto cloop; + } + if(match("noerror")) { + cflag |= NERR; + goto cloop; + } + if(match("sync")) { + cflag |= SYNC; + goto cloop; + } + } + fprint(2, "dd: bad arg: %s\n", key); + exits("arg"); + } + if(conv == null && cflag&(LCASE|UCASE)) + conv = cnull; + if(ifile) + ibf = open(ifile, 0); + else + ibf = dup(0, -1); + if(ibf < 0) { + fprint(2, "dd: open %s: %r\n", ifile); + exits("open"); + } + if(ofile){ + if(dotrunc) + obf = create(ofile, 1, 0664); + else + obf = open(ofile, 1); + if(obf < 0) { + fprint(2, "dd: create %s: %r\n", ofile); + exits("create"); + } + }else{ + obf = dup(1, -1); + if(obf < 0) { + fprint(2, "dd: can't dup file descriptor: %s: %r\n", ofile); + exits("dup"); + } + } + if(bs) + ibs = obs = bs; + if(ibs == obs && conv == null) + fflag++; + if(ibs == 0 || obs == 0) { + fprint(2, "dd: counts: cannot be zero\n"); + exits("counts"); + } + ibuf = sbrk(ibs); + if(fflag) + obuf = ibuf; + else + obuf = sbrk(obs); + sbrk(64); /* For good measure */ + if(ibuf == (char *)-1 || obuf == (char *)-1) { + fprint(2, "dd: not enough memory: %r\n"); + exits("memory"); + } + ibc = 0; + obc = 0; + cbc = 0; + op = obuf; + +/* + if(signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, term); +*/ + seek(obf, obs*oseekn, 1); + seek(ibf, ibs*iseekn, 1); + while(skip) { + read(ibf, ibuf, ibs); + skip--; + } + + ip = 0; +loop: + if(ibc-- == 0) { + ibc = 0; + if(count==0 || nifr+nipr!=count) { + if(cflag&(NERR|SYNC)) + for(ip=ibuf+ibs; ip>ibuf;) + *--ip = 0; + ibc = read(ibf, ibuf, ibs); + } + if(ibc == -1) { + perror("read"); + if((cflag&NERR) == 0) { + flsh(); + term(); + } + ibc = 0; + for(c=0; c<ibs; c++) + if(ibuf[c] != 0) + ibc = c; + stats(); + } + if(ibc == 0 && --files<=0) { + flsh(); + term(); + } + if(ibc != ibs) { + nipr++; + if(cflag&SYNC) + ibc = ibs; + } else + nifr++; + ip = ibuf; + c = (ibc>>1) & ~1; + if(cflag&SWAB && c) + do { + a = *ip++; + ip[-1] = *ip; + *ip++ = a; + } while(--c); + ip = ibuf; + if(fflag) { + obc = ibc; + flsh(); + ibc = 0; + } + goto loop; + } + c = 0; + c |= *ip++; + c &= 0377; + (*conv)(c); + goto loop; +} + +void +flsh(void) +{ + int c; + + if(obc) { + c = write(obf, obuf, obc); + if(c != obc) { + if(c > 0) + ++nopr; + perror("write"); + term(); + } + if(obc == obs) + nofr++; + else + nopr++; + obc = 0; + } +} + +int +match(char *s) +{ + char *cs; + + cs = string; + while(*cs++ == *s) + if(*s++ == '\0') + goto true; + if(*s != '\0') + return 0; + +true: + cs--; + string = cs; + return 1; +} + +vlong +number(long big) +{ + char *cs; + vlong n; + + cs = string; + n = 0; + while(*cs >= '0' && *cs <= '9') + n = n*10 + *cs++ - '0'; + for(;;) + switch(*cs++) { + + case 'k': + n *= 1024; + continue; + +/* case 'w': + n *= sizeof(int); + continue; +*/ + + case 'b': + n *= 512; + continue; + +/* case '*':*/ + case 'x': + string = cs; + n *= number(BIG); + + case '\0': + if(n>=big || n<0) { + fprint(2, "dd: argument %lld out of range\n", n); + exits("range"); + } + return n; + } + /* never gets here */ +} + +void +cnull(int cc) +{ + int c; + + c = cc; + if((cflag&UCASE) && c>='a' && c<='z') + c += 'A'-'a'; + if((cflag&LCASE) && c>='A' && c<='Z') + c += 'a'-'A'; + null(c); +} + +void +null(int c) +{ + + *op = c; + op++; + if(++obc >= obs) { + flsh(); + op = obuf; + } +} + +void +ascii(int cc) +{ + int c; + + c = etoa[cc]; + if(cbs == 0) { + cnull(c); + return; + } + if(c == ' ') { + nspace++; + goto out; + } + while(nspace > 0) { + null(' '); + nspace--; + } + cnull(c); + +out: + if(++cbc >= cbs) { + null('\n'); + cbc = 0; + nspace = 0; + } +} + +void +unblock(int cc) +{ + int c; + + c = cc & 0377; + if(cbs == 0) { + cnull(c); + return; + } + if(c == ' ') { + nspace++; + goto out; + } + while(nspace > 0) { + null(' '); + nspace--; + } + cnull(c); + +out: + if(++cbc >= cbs) { + null('\n'); + cbc = 0; + nspace = 0; + } +} + +void +ebcdic(int cc) +{ + int c; + + c = cc; + if(cflag&UCASE && c>='a' && c<='z') + c += 'A'-'a'; + if(cflag&LCASE && c>='A' && c<='Z') + c += 'a'-'A'; + c = atoe[c]; + if(cbs == 0) { + null(c); + return; + } + if(cc == '\n') { + while(cbc < cbs) { + null(atoe[' ']); + cbc++; + } + cbc = 0; + return; + } + if(cbc == cbs) + ntrunc++; + cbc++; + if(cbc <= cbs) + null(c); +} + +void +ibm(int cc) +{ + int c; + + c = cc; + if(cflag&UCASE && c>='a' && c<='z') + c += 'A'-'a'; + if(cflag&LCASE && c>='A' && c<='Z') + c += 'a'-'A'; + c = atoibm[c] & 0377; + if(cbs == 0) { + null(c); + return; + } + if(cc == '\n') { + while(cbc < cbs) { + null(atoibm[' ']); + cbc++; + } + cbc = 0; + return; + } + if(cbc == cbs) + ntrunc++; + cbc++; + if(cbc <= cbs) + null(c); +} + +void +block(int cc) +{ + int c; + + c = cc; + if(cflag&UCASE && c>='a' && c<='z') + c += 'A'-'a'; + if(cflag&LCASE && c>='A' && c<='Z') + c += 'a'-'A'; + c &= 0377; + if(cbs == 0) { + null(c); + return; + } + if(cc == '\n') { + while(cbc < cbs) { + null(' '); + cbc++; + } + cbc = 0; + return; + } + if(cbc == cbs) + ntrunc++; + cbc++; + if(cbc <= cbs) + null(c); +} + +void +term(void) +{ + + stats(); + exits(0); +} + +void +stats(void) +{ + + fprint(2, "%lud+%lud records in\n", nifr, nipr); + fprint(2, "%lud+%lud records out\n", nofr, nopr); + if(ntrunc) + fprint(2, "%lud truncated records\n", ntrunc); +} + +uchar etoa[] = +{ + 0000,0001,0002,0003,0234,0011,0206,0177, + 0227,0215,0216,0013,0014,0015,0016,0017, + 0020,0021,0022,0023,0235,0205,0010,0207, + 0030,0031,0222,0217,0034,0035,0036,0037, + 0200,0201,0202,0203,0204,0012,0027,0033, + 0210,0211,0212,0213,0214,0005,0006,0007, + 0220,0221,0026,0223,0224,0225,0226,0004, + 0230,0231,0232,0233,0024,0025,0236,0032, + 0040,0240,0241,0242,0243,0244,0245,0246, + 0247,0250,0133,0056,0074,0050,0053,0041, + 0046,0251,0252,0253,0254,0255,0256,0257, + 0260,0261,0135,0044,0052,0051,0073,0136, + 0055,0057,0262,0263,0264,0265,0266,0267, + 0270,0271,0174,0054,0045,0137,0076,0077, + 0272,0273,0274,0275,0276,0277,0300,0301, + 0302,0140,0072,0043,0100,0047,0075,0042, + 0303,0141,0142,0143,0144,0145,0146,0147, + 0150,0151,0304,0305,0306,0307,0310,0311, + 0312,0152,0153,0154,0155,0156,0157,0160, + 0161,0162,0313,0314,0315,0316,0317,0320, + 0321,0176,0163,0164,0165,0166,0167,0170, + 0171,0172,0322,0323,0324,0325,0326,0327, + 0330,0331,0332,0333,0334,0335,0336,0337, + 0340,0341,0342,0343,0344,0345,0346,0347, + 0173,0101,0102,0103,0104,0105,0106,0107, + 0110,0111,0350,0351,0352,0353,0354,0355, + 0175,0112,0113,0114,0115,0116,0117,0120, + 0121,0122,0356,0357,0360,0361,0362,0363, + 0134,0237,0123,0124,0125,0126,0127,0130, + 0131,0132,0364,0365,0366,0367,0370,0371, + 0060,0061,0062,0063,0064,0065,0066,0067, + 0070,0071,0372,0373,0374,0375,0376,0377, +}; +uchar atoe[] = +{ + 0000,0001,0002,0003,0067,0055,0056,0057, + 0026,0005,0045,0013,0014,0015,0016,0017, + 0020,0021,0022,0023,0074,0075,0062,0046, + 0030,0031,0077,0047,0034,0035,0036,0037, + 0100,0117,0177,0173,0133,0154,0120,0175, + 0115,0135,0134,0116,0153,0140,0113,0141, + 0360,0361,0362,0363,0364,0365,0366,0367, + 0370,0371,0172,0136,0114,0176,0156,0157, + 0174,0301,0302,0303,0304,0305,0306,0307, + 0310,0311,0321,0322,0323,0324,0325,0326, + 0327,0330,0331,0342,0343,0344,0345,0346, + 0347,0350,0351,0112,0340,0132,0137,0155, + 0171,0201,0202,0203,0204,0205,0206,0207, + 0210,0211,0221,0222,0223,0224,0225,0226, + 0227,0230,0231,0242,0243,0244,0245,0246, + 0247,0250,0251,0300,0152,0320,0241,0007, + 0040,0041,0042,0043,0044,0025,0006,0027, + 0050,0051,0052,0053,0054,0011,0012,0033, + 0060,0061,0032,0063,0064,0065,0066,0010, + 0070,0071,0072,0073,0004,0024,0076,0341, + 0101,0102,0103,0104,0105,0106,0107,0110, + 0111,0121,0122,0123,0124,0125,0126,0127, + 0130,0131,0142,0143,0144,0145,0146,0147, + 0150,0151,0160,0161,0162,0163,0164,0165, + 0166,0167,0170,0200,0212,0213,0214,0215, + 0216,0217,0220,0232,0233,0234,0235,0236, + 0237,0240,0252,0253,0254,0255,0256,0257, + 0260,0261,0262,0263,0264,0265,0266,0267, + 0270,0271,0272,0273,0274,0275,0276,0277, + 0312,0313,0314,0315,0316,0317,0332,0333, + 0334,0335,0336,0337,0352,0353,0354,0355, + 0356,0357,0372,0373,0374,0375,0376,0377, +}; +uchar atoibm[] = +{ + 0000,0001,0002,0003,0067,0055,0056,0057, + 0026,0005,0045,0013,0014,0015,0016,0017, + 0020,0021,0022,0023,0074,0075,0062,0046, + 0030,0031,0077,0047,0034,0035,0036,0037, + 0100,0132,0177,0173,0133,0154,0120,0175, + 0115,0135,0134,0116,0153,0140,0113,0141, + 0360,0361,0362,0363,0364,0365,0366,0367, + 0370,0371,0172,0136,0114,0176,0156,0157, + 0174,0301,0302,0303,0304,0305,0306,0307, + 0310,0311,0321,0322,0323,0324,0325,0326, + 0327,0330,0331,0342,0343,0344,0345,0346, + 0347,0350,0351,0255,0340,0275,0137,0155, + 0171,0201,0202,0203,0204,0205,0206,0207, + 0210,0211,0221,0222,0223,0224,0225,0226, + 0227,0230,0231,0242,0243,0244,0245,0246, + 0247,0250,0251,0300,0117,0320,0241,0007, + 0040,0041,0042,0043,0044,0025,0006,0027, + 0050,0051,0052,0053,0054,0011,0012,0033, + 0060,0061,0032,0063,0064,0065,0066,0010, + 0070,0071,0072,0073,0004,0024,0076,0341, + 0101,0102,0103,0104,0105,0106,0107,0110, + 0111,0121,0122,0123,0124,0125,0126,0127, + 0130,0131,0142,0143,0144,0145,0146,0147, + 0150,0151,0160,0161,0162,0163,0164,0165, + 0166,0167,0170,0200,0212,0213,0214,0215, + 0216,0217,0220,0232,0233,0234,0235,0236, + 0237,0240,0252,0253,0254,0255,0256,0257, + 0260,0261,0262,0263,0264,0265,0266,0267, + 0270,0271,0272,0273,0274,0275,0276,0277, + 0312,0313,0314,0315,0316,0317,0332,0333, + 0334,0335,0336,0337,0352,0353,0354,0355, + 0356,0357,0372,0373,0374,0375,0376,0377, +}; diff --git a/src/cmd/deroff.c b/src/cmd/deroff.c new file mode 100644 index 00000000..914c5a3f --- /dev/null +++ b/src/cmd/deroff.c @@ -0,0 +1,969 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +/* + * Deroff command -- strip troff, eqn, and tbl sequences from + * a file. Has three flags argument, -w, to cause output one word per line + * rather than in the original format. + * -mm (or -ms) causes the corresponding macro's to be interpreted + * so that just sentences are output + * -ml also gets rid of lists. + * -i causes deroff to ignore .so and .nx commands. + * Deroff follows .so and .nx commands, removes contents of macro + * definitions, equations (both .EQ ... .EN and $...$), + * Tbl command sequences, and Troff backslash vconstructions. + * + * All input is through the C macro; the most recently read character is in c. + */ + +/* +#define C ((c = Bgetrune(infile)) < 0?\ + eof():\ + ((c == ldelim) && (filesp == files)?\ + skeqn():\ + (c == '\n'?\ + (linect++,c):\ + c))) + +#define C1 ((c = Bgetrune(infile)) == Beof?\ + eof():\ + (c == '\n'?\ + (linect++,c):\ + c)) +*/ + +/* lose those macros! */ +#define C fC() +#define C1 fC1() + +#define SKIP while(C != '\n') +#define SKIP1 while(C1 != '\n') +#define SKIP_TO_COM SKIP;\ + SKIP;\ + pc=c;\ + while(C != '.' || pc != '\n' || C > 'Z')\ + pc=c + +#define YES 1 +#define NO 0 +#define MS 0 +#define MM 1 +#define ONE 1 +#define TWO 2 + +#define NOCHAR -2 +#define EXTENDED -1 /* All runes above 0x7F */ +#define SPECIAL 0 +#define APOS 1 +#define PUNCT 2 +#define DIGIT 3 +#define LETTER 4 + + +int linect = 0; +int wordflag= NO; +int underscoreflag = NO; +int msflag = NO; +int iflag = NO; +int mac = MM; +int disp = 0; +int inmacro = NO; +int intable = NO; +int eqnflag = 0; + +#define MAX_ASCII 0X80 + +char chars[MAX_ASCII]; /* SPECIAL, PUNCT, APOS, DIGIT, or LETTER */ + +Rune line[30000]; +Rune* lp; + +long c; +long pc; +int ldelim = NOCHAR; +int rdelim = NOCHAR; + + +char** argv; + +char fname[50]; +Biobuf* files[15]; +Biobuf**filesp; +Biobuf* infile; +char* devnull = "/dev/null"; +Biobuf *infile; +Biobuf bout; + +long skeqn(void); +Biobuf* opn(char *p); +int eof(void); +int charclass(int); +void getfname(void); +void fatal(char *s, char *p); +void usage(void); +void work(void); +void putmac(Rune *rp, int vconst); +void regline(int macline, int vconst); +void putwords(void); +void comline(void); +void macro(void); +void eqn(void); +void tbl(void); +void stbl(void); +void sdis(char a1, char a2); +void sce(void); +void backsl(void); +char* copys(char *s); +void refer(int c1); +void inpic(void); + +int +fC(void) +{ + c = Bgetrune(infile); + if(c < 0) + return eof(); + if(c == ldelim && filesp == files) + return skeqn(); + if(c == '\n') + linect++; + return c; +} + +int +fC1(void) +{ + c = Bgetrune(infile); + if(c == Beof) + return eof(); + if(c == '\n') + linect++; + return c; +} + +void +main(int argc, char *av[]) +{ + int i; + char *f; + + argv = av; + Binit(&bout, 1, OWRITE); + ARGBEGIN{ + case 'w': + wordflag = YES; + break; + case '_': + wordflag = YES; + underscoreflag = YES; + break; + case 'm': + msflag = YES; + if(f = ARGF()) + switch(*f) + { + case 'm': mac = MM; break; + case 's': mac = MS; break; + case 'l': disp = 1; break; + default: usage(); + } + else + usage(); + break; + case 'i': + iflag = YES; + break; + default: + usage(); + }ARGEND + if(*argv) + infile = opn(*argv++); + else{ + infile = malloc(sizeof(Biobuf)); + Binit(infile, 0, OREAD); + } + files[0] = infile; + filesp = &files[0]; + + for(i='a'; i<='z' ; ++i) + chars[i] = LETTER; + for(i='A'; i<='Z'; ++i) + chars[i] = LETTER; + for(i='0'; i<='9'; ++i) + chars[i] = DIGIT; + chars['\''] = APOS; + chars['&'] = APOS; + chars['\b'] = APOS; + chars['.'] = PUNCT; + chars[','] = PUNCT; + chars[';'] = PUNCT; + chars['?'] = PUNCT; + chars[':'] = PUNCT; + work(); +} + +long +skeqn(void) +{ + while(C1 != rdelim) + if(c == '\\') + c = C1; + else if(c == '"') + while(C1 != '"') + if(c == '\\') + C1; + if (msflag) + eqnflag = 1; + return(c = ' '); +} + +Biobuf* +opn(char *p) +{ + Biobuf *fd; + + while ((fd = Bopen(p, OREAD)) == 0) { + if(msflag || p == devnull) + fatal("Cannot open file %s - quitting\n", p); + else { + fprint(2, "Deroff: Cannot open file %s - continuing\n", p); + p = devnull; + } + } + linect = 0; + return(fd); +} + +int +eof(void) +{ + if(Bfildes(infile) != 0) + Bterm(infile); + if(filesp > files) + infile = *--filesp; + else + if(*argv) + infile = opn(*argv++); + else + exits(0); + return(C); +} + +void +getfname(void) +{ + char *p; + Rune r; + Dir *dir; + struct chain + { + struct chain* nextp; + char* datap; + } *q; + + static struct chain *namechain= 0; + + while(C == ' ') + ; + for(p = fname; (r=c) != '\n' && r != ' ' && r != '\t' && r != '\\'; C) + p += runetochar(p, &r); + *p = '\0'; + while(c != '\n') + C; + if(!strcmp(fname, "/sys/lib/tmac/tmac.cs") + || !strcmp(fname, "/sys/lib/tmac/tmac.s")) { + fname[0] = '\0'; + return; + } + dir = dirstat(fname); + if(dir!=nil && ((dir->mode & DMDIR) || dir->type != 'M')) { + free(dir); + fname[0] = '\0'; + return; + } + free(dir); + /* + * see if this name has already been used + */ + + for(q = namechain; q; q = q->nextp) + if( !strcmp(fname, q->datap)) { + fname[0] = '\0'; + return; + } + q = (struct chain*)malloc(sizeof(struct chain)); + q->nextp = namechain; + q->datap = copys(fname); + namechain = q; +} + +void +usage(void) +{ + fprint(2,"usage: deroff [-nw_pi] [-m (m s l)] [file ...] \n"); + exits("usage"); +} + +void +fatal(char *s, char *p) +{ + fprint(2, "deroff: "); + fprint(2, s, p); + exits(s); +} + +void +work(void) +{ + + for(;;) { + eqnflag = 0; + if(C == '.' || c == '\'') + comline(); + else + regline(NO, TWO); + } +} + +void +regline(int macline, int vconst) +{ + line[0] = c; + lp = line; + for(;;) { + if(c == '\\') { + *lp = ' '; + backsl(); + if(c == '%') /* no blank for hyphenation char */ + lp--; + } + if(c == '\n') + break; + if(intable && c=='T') { + *++lp = C; + if(c=='{' || c=='}') { + lp[-1] = ' '; + *lp = C; + } + } else { + if(msflag == 1 && eqnflag == 1) { + eqnflag = 0; + *++lp = 'x'; + } + *++lp = C; + } + } + *lp = '\0'; + if(lp != line) { + if(wordflag) + putwords(); + else + if(macline) + putmac(line,vconst); + else + Bprint(&bout, "%S\n", line); + } +} + +void +putmac(Rune *rp, int vconst) +{ + Rune *t; + int found; + Rune last; + + found = 0; + last = 0; + while(*rp) { + while(*rp == ' ' || *rp == '\t') + Bputrune(&bout, *rp++); + for(t = rp; *t != ' ' && *t != '\t' && *t != '\0'; t++) + ; + if(*rp == '\"') + rp++; + if(t > rp+vconst && charclass(*rp) == LETTER + && charclass(rp[1]) == LETTER) { + while(rp < t) + if(*rp == '\"') + rp++; + else + Bputrune(&bout, *rp++); + last = t[-1]; + found++; + } else + if(found && charclass(*rp) == PUNCT && rp[1] == '\0') + Bputrune(&bout, *rp++); + else { + last = t[-1]; + rp = t; + } + } + Bputc(&bout, '\n'); + if(msflag && charclass(last) == PUNCT) + Bprint(&bout, " %C\n", last); +} + +/* + * break into words for -w option + */ +void +putwords(void) +{ + Rune *p, *p1; + int i, nlet; + + + for(p1 = line;;) { + /* + * skip initial specials ampersands and apostrophes + */ + while((i = charclass(*p1)) != EXTENDED && i < DIGIT) + if(*p1++ == '\0') + return; + nlet = 0; + for(p = p1; (i = charclass(*p)) != SPECIAL || (underscoreflag && *p=='_'); p++) + if(i == LETTER || (underscoreflag && *p == '_')) + nlet++; + /* + * MDM definition of word + */ + if(nlet > 1) { + /* + * delete trailing ampersands and apostrophes + */ + while(*--p == '\'' || *p == '&' + || charclass(*p) == PUNCT) + ; + while(p1 <= p) + Bputrune(&bout, *p1++); + Bputc(&bout, '\n'); + } else + p1 = p; + } +} + +void +comline(void) +{ + long c1, c2; + + while(C==' ' || c=='\t') + ; +comx: + if((c1=c) == '\n') + return; + c2 = C; + if(c1=='.' && c2!='.') + inmacro = NO; + if(msflag && c1 == '['){ + refer(c2); + return; + } + if(c2 == '\n') + return; + if(c1 == '\\' && c2 == '\"') + SKIP; + else + if (filesp==files && c1=='E' && c2=='Q') + eqn(); + else + if(filesp==files && c1=='T' && (c2=='S' || c2=='C' || c2=='&')) { + if(msflag) + stbl(); + else + tbl(); + } + else + if(c1=='T' && c2=='E') + intable = NO; + else if (!inmacro && + ((c1 == 'd' && c2 == 'e') || + (c1 == 'i' && c2 == 'g') || + (c1 == 'a' && c2 == 'm'))) + macro(); + else + if(c1=='s' && c2=='o') { + if(iflag) + SKIP; + else { + getfname(); + if(fname[0]) { + if(infile = opn(fname)) + *++filesp = infile; + else infile = *filesp; + } + } + } + else + if(c1=='n' && c2=='x') + if(iflag) + SKIP; + else { + getfname(); + if(fname[0] == '\0') + exits(0); + if(Bfildes(infile) != 0) + Bterm(infile); + infile = *filesp = opn(fname); + } + else + if(c1 == 't' && c2 == 'm') + SKIP; + else + if(c1=='h' && c2=='w') + SKIP; + else + if(msflag && c1 == 'T' && c2 == 'L') { + SKIP_TO_COM; + goto comx; + } + else + if(msflag && c1=='N' && c2 == 'R') + SKIP; + else + if(msflag && c1 == 'A' && (c2 == 'U' || c2 == 'I')){ + if(mac==MM)SKIP; + else { + SKIP_TO_COM; + goto comx; + } + } else + if(msflag && c1=='F' && c2=='S') { + SKIP_TO_COM; + goto comx; + } + else + if(msflag && (c1=='S' || c1=='N') && c2=='H') { + SKIP_TO_COM; + goto comx; + } else + if(c1 == 'U' && c2 == 'X') { + if(wordflag) + Bprint(&bout, "UNIX\n"); + else + Bprint(&bout, "UNIX "); + } else + if(msflag && c1=='O' && c2=='K') { + SKIP_TO_COM; + goto comx; + } else + if(msflag && c1=='N' && c2=='D') + SKIP; + else + if(msflag && mac==MM && c1=='H' && (c2==' '||c2=='U')) + SKIP; + else + if(msflag && mac==MM && c2=='L') { + if(disp || c1=='R') + sdis('L', 'E'); + else { + SKIP; + Bprint(&bout, " ."); + } + } else + if(!msflag && c1=='P' && c2=='S') { + inpic(); + } else + if(msflag && (c1=='D' || c1=='N' || c1=='K'|| c1=='P') && c2=='S') { + sdis(c1, 'E'); + } else + if(msflag && (c1 == 'K' && c2 == 'F')) { + sdis(c1,'E'); + } else + if(msflag && c1=='n' && c2=='f') + sdis('f','i'); + else + if(msflag && c1=='c' && c2=='e') + sce(); + else { + if(c1=='.' && c2=='.') { + if(msflag) { + SKIP; + return; + } + while(C == '.') + ; + } + inmacro++; + if(c1 <= 'Z' && msflag) + regline(YES,ONE); + else { + if(wordflag) + C; + regline(YES,TWO); + } + inmacro--; + } +} + +void +macro(void) +{ + if(msflag) { + do { + SKIP1; + } while(C1 != '.' || C1 != '.' || C1 == '.'); + if(c != '\n') + SKIP; + return; + } + SKIP; + inmacro = YES; +} + +void +sdis(char a1, char a2) +{ + int c1, c2; + int eqnf; + int lct; + + if(a1 == 'P'){ + while(C1 == ' ') + ; + if(c == '<') { + SKIP1; + return; + } + } + lct = 0; + eqnf = 1; + if(c != '\n') + SKIP1; + for(;;) { + while(C1 != '.') + if(c == '\n') + continue; + else + SKIP1; + if((c1=C1) == '\n') + continue; + if((c2=C1) == '\n') { + if(a1 == 'f' && (c1 == 'P' || c1 == 'H')) + return; + continue; + } + if(c1==a1 && c2 == a2) { + SKIP1; + if(lct != 0){ + lct--; + continue; + } + if(eqnf) + Bprint(&bout, " ."); + Bputc(&bout, '\n'); + return; + } else + if(a1 == 'L' && c2 == 'L') { + lct++; + SKIP1; + } else + if(a1 == 'D' && c1 == 'E' && c2 == 'Q') { + eqn(); + eqnf = 0; + } else + if(a1 == 'f') { + if((mac == MS && c2 == 'P') || + (mac == MM && c1 == 'H' && c2 == 'U')){ + SKIP1; + return; + } + SKIP1; + } + else + SKIP1; + } +} + +void +tbl(void) +{ + while(C != '.') + ; + SKIP; + intable = YES; +} + +void +stbl(void) +{ + while(C != '.') + ; + SKIP_TO_COM; + if(c != 'T' || C != 'E') { + SKIP; + pc = c; + while(C != '.' || pc != '\n' || C != 'T' || C != 'E') + pc = c; + } +} + +void +eqn(void) +{ + long c1, c2; + int dflg; + char last; + + last = 0; + dflg = 1; + SKIP; + + for(;;) { + if(C1 == '.' || c == '\'') { + while(C1==' ' || c=='\t') + ; + if(c=='E' && C1=='N') { + SKIP; + if(msflag && dflg) { + Bputc(&bout, 'x'); + Bputc(&bout, ' '); + if(last) { + Bputc(&bout, last); + Bputc(&bout, '\n'); + } + } + return; + } + } else + if(c == 'd') { + if(C1=='e' && C1=='l') + if(C1=='i' && C1=='m') { + while(C1 == ' ') + ; + if((c1=c)=='\n' || (c2=C1)=='\n' || + (c1=='o' && c2=='f' && C1=='f')) { + ldelim = NOCHAR; + rdelim = NOCHAR; + } else { + ldelim = c1; + rdelim = c2; + } + } + dflg = 0; + } + if(c != '\n') + while(C1 != '\n') { + if(chars[c] == PUNCT) + last = c; + else + if(c != ' ') + last = 0; + } + } +} + +/* + * skip over a complete backslash vconstruction + */ +void +backsl(void) +{ + int bdelim; + +sw: + switch(C1) + { + case '"': + SKIP1; + return; + + case 's': + if(C1 == '\\') + backsl(); + else { + while(C1>='0' && c<='9') + ; + Bungetrune(infile); + c = '0'; + } + lp--; + return; + + case 'f': + case 'n': + case '*': + if(C1 != '(') + return; + + case '(': + if(msflag) { + if(C == 'e') { + if(C1 == 'm') { + *lp = '-'; + return; + } + } else + if(c != '\n') + C1; + return; + } + if(C1 != '\n') + C1; + return; + + case '$': + C1; /* discard argument number */ + return; + + case 'b': + case 'x': + case 'v': + case 'h': + case 'w': + case 'o': + case 'l': + case 'L': + if((bdelim=C1) == '\n') + return; + while(C1!='\n' && c!=bdelim) + if(c == '\\') + backsl(); + return; + + case '\\': + if(inmacro) + goto sw; + default: + return; + } +} + +char* +copys(char *s) +{ + char *t, *t0; + + if((t0 = t = malloc((strlen(s)+1))) == 0) + fatal("Cannot allocate memory", (char*)0); + while(*t++ = *s++) + ; + return(t0); +} + +void +sce(void) +{ + int n = 1; + + while (C != L'\n' && !(L'0' <= c && c <= L'9')) + ; + if (c != L'\n') { + for (n = c-L'0';'0' <= C && c <= L'9';) + n = n*10 + c-L'0'; + } + while(n) { + if(C == '.') { + if(C == 'c') { + if(C == 'e') { + while(C == ' ') + ; + if(c == '0') { + SKIP; + break; + } else + SKIP; + } else + SKIP; + } else + if(c == 'P' || C == 'P') { + if(c != '\n') + SKIP; + break; + } else + if(c != '\n') + SKIP; + } else { + SKIP; + n--; + } + } +} + +void +refer(int c1) +{ + int c2; + + if(c1 != '\n') + SKIP; + c2 = 0; + for(;;) { + if(C != '.') + SKIP; + else { + if(C != ']') + SKIP; + else { + while(C != '\n') + c2 = c; + if(charclass(c2) == PUNCT) + Bprint(&bout, " %C",c2); + return; + } + } + } +} + +void +inpic(void) +{ + int c1; + Rune *p1; + +/* SKIP1;*/ + while(C1 != '\n') + if(c == '<'){ + SKIP1; + return; + } + p1 = line; + c = '\n'; + for(;;) { + c1 = c; + if(C1 == '.' && c1 == '\n') { + if(C1 != 'P' || C1 != 'E') { + if(c != '\n'){ + SKIP1; + c = '\n'; + } + continue; + } + SKIP1; + return; + } else + if(c == '\"') { + while(C1 != '\"') { + if(c == '\\') { + if(C1 == '\"') + continue; + Bungetrune(infile); + backsl(); + } else + *p1++ = c; + } + *p1++ = ' '; + } else + if(c == '\n' && p1 != line) { + *p1 = '\0'; + if(wordflag) + putwords(); + else + Bprint(&bout, "%S\n\n", line); + p1 = line; + } + } +} + +int +charclass(int c) +{ + if(c < MAX_ASCII) + return chars[c]; + switch(c){ + case 0x2013: case 0x2014: /* en dash, em dash */ + return SPECIAL; + } + return EXTENDED; +} diff --git a/src/cmd/du.C b/src/cmd/du.C new file mode 100644 index 00000000..3a96c598 --- /dev/null +++ b/src/cmd/du.C @@ -0,0 +1,194 @@ +#include <u.h> +#include <libc.h> + +extern vlong du(char*, Dir*); +extern vlong k(vlong); +extern void err(char*); +extern int warn(char*); +extern int seen(Dir*); + +int aflag; +int fflag; +int nflag; +int sflag; +int tflag; +int uflag; +int qflag; +char *fmt = "%llud\t%s\n"; +vlong blocksize = 1024LL; + +void +main(int argc, char *argv[]) +{ + int i; + char *s, *ss; + + ARGBEGIN { + case 'a': /* all files */ + aflag = 1; + break; + case 's': /* only top level */ + sflag = 1; + break; + case 'f': /* ignore errors */ + fflag = 1; + break; + case 'n': /* all files, number of bytes */ + aflag = 1; + nflag = 1; + break; + case 't': /* return modified/accessed time */ + tflag = 1; + break; + case 'u': /* accessed time */ + uflag = 1; + break; + case 'q': /* qid */ + fmt = "%.16llux\t%s\n"; + qflag = 1; + break; + case 'b': /* block size */ + s = ARGF(); + if(s) { + blocksize = strtoul(s, &ss, 0); + if(s == ss) + blocksize = 1; + if(*ss == 'k') + blocksize *= 1024; + } + break; + } ARGEND + if(argc==0) + print(fmt, du(".", dirstat(".")), "."); + else + for(i=0; i<argc; i++) + print(fmt, du(argv[i], dirstat(argv[i])), argv[i]); + exits(0); +} + +vlong +du(char *name, Dir *dir) +{ + int fd, i, n; + Dir *buf, *d; + char file[256]; + vlong nk, t; + + if(dir == nil) + return warn(name); + + fd = open(name, OREAD); + if(fd < 0) + return warn(name); + + if((dir->qid.type&QTDIR) == 0) + nk = k(dir->length); + else{ + nk = 0; + while((n=dirread(fd, &buf)) > 0) { + d = buf; + for(i=0; i<n; i++, d++) { + if((d->qid.type&QTDIR) == 0) { + t = k(d->length); + nk += t; + if(aflag) { + sprint(file, "%s/%s", name, d->name); + if(tflag) { + t = d->mtime; + if(uflag) + t = d->atime; + } + if(qflag) + t = d->qid.path; + print(fmt, t, file); + } + continue; + } + if(strcmp(d->name, ".") == 0 || + strcmp(d->name, "..") == 0 || + seen(d)) + continue; + sprint(file, "%s/%s", name, d->name); + t = du(file, d); + nk += t; + if(tflag) { + t = d->mtime; + if(uflag) + t = d->atime; + } + if(qflag) + t = d->qid.path; + if(!sflag) + print(fmt, t, file); + } + free(buf); + } + if(n < 0) + warn(name); + } + close(fd); + if(tflag) { + if(uflag) + return dir->atime; + return dir->mtime; + } + if(qflag) + return dir->qid.path; + return nk; +} + +#define NCACHE 128 /* must be power of two */ +typedef struct Cache Cache; +struct Cache +{ + Dir* cache; + int n; + int max; +} cache[NCACHE]; + +int +seen(Dir *dir) +{ + Dir *dp; + int i; + Cache *c; + + c = &cache[dir->qid.path&(NCACHE-1)]; + dp = c->cache; + for(i=0; i<c->n; i++, dp++) + if(dir->qid.path == dp->qid.path && + dir->type == dp->type && + dir->dev == dp->dev) + return 1; + if(c->n == c->max){ + c->cache = realloc(c->cache, (c->max+=20)*sizeof(Dir)); + if(cache == 0) + err("malloc failure"); + } + c->cache[c->n++] = *dir; + return 0; +} + +void +err(char *s) +{ + fprint(2, "du: %s: %r\n", s); + exits(s); +} + +int +warn(char *s) +{ + if(fflag == 0) + fprint(2, "du: %s: %r\n", s); + return 0; +} + +vlong +k(vlong n) +{ + if(nflag) + return n; + n = (n+blocksize-1)/blocksize; + return n*blocksize/1024LL; +} diff --git a/src/cmd/echo.c b/src/cmd/echo.c new file mode 100644 index 00000000..25b69692 --- /dev/null +++ b/src/cmd/echo.c @@ -0,0 +1,38 @@ +#include <u.h> +#include <libc.h> + +void +main(int argc, char *argv[]) +{ + int nflag; + int i, len; + char *buf, *p; + + nflag = 0; + if(argc > 1 && strcmp(argv[1], "-n") == 0) + nflag = 1; + + len = 1; + for(i = 1+nflag; i < argc; i++) + len += strlen(argv[i])+1; + + buf = malloc(len); + if(buf == 0) + exits("no memory"); + + p = buf; + for(i = 1+nflag; i < argc; i++){ + strcpy(p, argv[i]); + p += strlen(p); + if(i < argc-1) + *p++ = ' '; + } + + if(!nflag) + *p++ = '\n'; + + if(write(1, buf, p-buf) < 0) + fprint(2, "echo: write error: %r\n"); + + exits((char *)0); +} diff --git a/src/cmd/ed.c b/src/cmd/ed.c new file mode 100644 index 00000000..42fa3e07 --- /dev/null +++ b/src/cmd/ed.c @@ -0,0 +1,1608 @@ +/* + * Editor + */ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <regexp.h> + +enum +{ + FNSIZE = 128, /* file name */ + LBSIZE = 4096, /* max line size */ + BLKSIZE = 4096, /* block size in temp file */ + NBLK = 8191, /* max size of temp file */ + ESIZE = 256, /* max size of reg exp */ + GBSIZE = 256, /* max size of global command */ + MAXSUB = 9, /* max number of sub reg exp */ + ESCFLG = 0xFFFF, /* escape Rune - user defined code */ + EOF = -1, +}; + +void (*oldhup)(int); +void (*oldquit)(int); +int* addr1; +int* addr2; +int anymarks; +int col; +long count; +int* dol; +int* dot; +int fchange; +char file[FNSIZE]; +Rune genbuf[LBSIZE]; +int given; +Rune* globp; +int iblock; +int ichanged; +int io; +Biobuf iobuf; +int lastc; +char line[70]; +Rune* linebp; +Rune linebuf[LBSIZE]; +int listf; +int listn; +Rune* loc1; +Rune* loc2; +int names[26]; +int nleft; +int oblock; +int oflag; +Reprog *pattern; +int peekc; +int pflag; +int rescuing; +Rune rhsbuf[LBSIZE/2]; +char savedfile[FNSIZE]; +jmp_buf savej; +int subnewa; +int subolda; +Resub subexp[MAXSUB]; +char* tfname; +int tline; +int waiting; +int wrapp; +int* zero; + +char Q[] = ""; +char T[] = "TMP"; +char WRERR[] = "WRITE ERROR"; +int bpagesize = 20; +char hex[] = "0123456789abcdef"; +char* linp = line; +ulong nlall = 128; +int tfile = -1; +int vflag = 1; + +void add(int); +int* address(void); +int append(int(*)(void), int*); +void browse(void); +void callunix(void); +void commands(void); +void compile(int); +int compsub(void); +void dosub(void); +void error(char*); +int match(int*); +void exfile(int); +void filename(int); +Rune* getblock(int, int); +int getchr(void); +int getcopy(void); +int getfile(void); +Rune* getline(int); +int getnum(void); +int getsub(void); +int gettty(void); +void global(int); +void init(void); +void join(void); +void move(int); +void newline(void); +void nonzero(void); +void notifyf(void*, char*); +Rune* place(Rune*, Rune*, Rune*); +void printcom(void); +void putchr(int); +void putd(void); +void putfile(void); +int putline(void); +void putshst(Rune*); +void putst(char*); +void quit(void); +void rdelete(int*, int*); +void regerror(char *); +void reverse(int*, int*); +void setnoaddr(void); +void setwide(void); +void squeeze(int); +void substitute(int); + +Rune La[] = { 'a', 0 }; +Rune Lr[] = { 'r', 0 }; + +char tmp[] = "/tmp/eXXXXX"; + +void +main(int argc, char *argv[]) +{ + char *p1, *p2; + + notify(notifyf); + ARGBEGIN { + case 'o': + oflag = 1; + vflag = 0; + break; + } ARGEND + + USED(argc); + if(*argv && (strcmp(*argv, "-") == 0)) { + argv++; + vflag = 0; + } + if(oflag) { + p1 = "/fd/1"; + p2 = savedfile; + while(*p2++ = *p1++) + ; + globp = La; + } else + if(*argv) { + p1 = *argv; + p2 = savedfile; + while(*p2++ = *p1++) + if(p2 >= &savedfile[sizeof(savedfile)]) + p2--; + globp = Lr; + } + zero = malloc((nlall+5)*sizeof(int*)); + tfname = mktemp(tmp); + init(); + setjmp(savej); + commands(); + quit(); +} + +void +commands(void) +{ + int *a1, c, temp; + char lastsep; + Dir *d; + + for(;;) { + if(pflag) { + pflag = 0; + addr1 = addr2 = dot; + printcom(); + } + c = '\n'; + for(addr1 = 0;;) { + lastsep = c; + a1 = address(); + c = getchr(); + if(c != ',' && c != ';') + break; + if(lastsep == ',') + error(Q); + if(a1 == 0) { + a1 = zero+1; + if(a1 > dol) + a1--; + } + addr1 = a1; + if(c == ';') + dot = a1; + } + if(lastsep != '\n' && a1 == 0) + a1 = dol; + if((addr2=a1) == 0) { + given = 0; + addr2 = dot; + } else + given = 1; + if(addr1 == 0) + addr1 = addr2; + switch(c) { + + case 'a': + add(0); + continue; + + case 'b': + nonzero(); + browse(); + continue; + + case 'c': + nonzero(); + newline(); + rdelete(addr1, addr2); + append(gettty, addr1-1); + continue; + + case 'd': + nonzero(); + newline(); + rdelete(addr1, addr2); + continue; + + case 'E': + fchange = 0; + c = 'e'; + case 'e': + setnoaddr(); + if(vflag && fchange) { + fchange = 0; + error(Q); + } + filename(c); + init(); + addr2 = zero; + goto caseread; + + case 'f': + setnoaddr(); + filename(c); + putst(savedfile); + continue; + + case 'g': + global(1); + continue; + + case 'i': + add(-1); + continue; + + + case 'j': + if(!given) + addr2++; + newline(); + join(); + continue; + + case 'k': + nonzero(); + c = getchr(); + if(c < 'a' || c > 'z') + error(Q); + newline(); + names[c-'a'] = *addr2 & ~01; + anymarks |= 01; + continue; + + case 'm': + move(0); + continue; + + case 'n': + listn++; + newline(); + printcom(); + continue; + + case '\n': + if(a1==0) { + a1 = dot+1; + addr2 = a1; + addr1 = a1; + } + if(lastsep==';') + addr1 = a1; + printcom(); + continue; + + case 'l': + listf++; + case 'p': + case 'P': + newline(); + printcom(); + continue; + + case 'Q': + fchange = 0; + case 'q': + setnoaddr(); + newline(); + quit(); + + case 'r': + filename(c); + caseread: + if((io=open(file, OREAD)) < 0) { + lastc = '\n'; + error(file); + } + if((d = dirfstat(io)) != nil){ + if(d->mode & DMAPPEND) + print("warning: %s is append only\n", file); + free(d); + } + Binit(&iobuf, io, OREAD); + setwide(); + squeeze(0); + c = zero != dol; + append(getfile, addr2); + exfile(OREAD); + + fchange = c; + continue; + + case 's': + nonzero(); + substitute(globp != 0); + continue; + + case 't': + move(1); + continue; + + case 'u': + nonzero(); + newline(); + if((*addr2&~01) != subnewa) + error(Q); + *addr2 = subolda; + dot = addr2; + continue; + + case 'v': + global(0); + continue; + + case 'W': + wrapp++; + case 'w': + setwide(); + squeeze(dol>zero); + temp = getchr(); + if(temp != 'q' && temp != 'Q') { + peekc = temp; + temp = 0; + } + filename(c); + if(!wrapp || + ((io = open(file, OWRITE)) == -1) || + ((seek(io, 0L, 2)) == -1)) + if((io = create(file, OWRITE, 0666)) < 0) + error(file); + Binit(&iobuf, io, OWRITE); + wrapp = 0; + if(dol > zero) + putfile(); + exfile(OWRITE); + if(addr1<=zero+1 && addr2==dol) + fchange = 0; + if(temp == 'Q') + fchange = 0; + if(temp) + quit(); + continue; + + case '=': + setwide(); + squeeze(0); + newline(); + count = addr2 - zero; + putd(); + putchr(L'\n'); + continue; + + case '!': + callunix(); + continue; + + case EOF: + return; + + } + error(Q); + } +} + +void +printcom(void) +{ + int *a1; + + nonzero(); + a1 = addr1; + do { + if(listn) { + count = a1-zero; + putd(); + putchr(L'\t'); + } + putshst(getline(*a1++)); + } while(a1 <= addr2); + dot = addr2; + listf = 0; + listn = 0; + pflag = 0; +} + +int* +address(void) +{ + int sign, *a, opcnt, nextopand, *b, c; + + nextopand = -1; + sign = 1; + opcnt = 0; + a = dot; + do { + do { + c = getchr(); + } while(c == ' ' || c == '\t'); + if(c >= '0' && c <= '9') { + peekc = c; + if(!opcnt) + a = zero; + a += sign*getnum(); + } else + switch(c) { + case '$': + a = dol; + case '.': + if(opcnt) + error(Q); + break; + case '\'': + c = getchr(); + if(opcnt || c < 'a' || c > 'z') + error(Q); + a = zero; + do { + a++; + } while(a <= dol && names[c-'a'] != (*a & ~01)); + break; + case '?': + sign = -sign; + case '/': + compile(c); + b = a; + for(;;) { + a += sign; + if(a <= zero) + a = dol; + if(a > dol) + a = zero; + if(match(a)) + break; + if(a == b) + error(Q); + } + break; + default: + if(nextopand == opcnt) { + a += sign; + if(a < zero || dol < a) + continue; /* error(Q); */ + } + if(c != '+' && c != '-' && c != '^') { + peekc = c; + if(opcnt == 0) + a = 0; + return a; + } + sign = 1; + if(c != '+') + sign = -sign; + nextopand = ++opcnt; + continue; + } + sign = 1; + opcnt++; + } while(zero <= a && a <= dol); + error(Q); + return 0; +} + +int +getnum(void) +{ + int r, c; + + r = 0; + for(;;) { + c = getchr(); + if(c < '0' || c > '9') + break; + r = r*10 + (c-'0'); + } + peekc = c; + return r; +} + +void +setwide(void) +{ + if(!given) { + addr1 = zero + (dol>zero); + addr2 = dol; + } +} + +void +setnoaddr(void) +{ + if(given) + error(Q); +} + +void +nonzero(void) +{ + squeeze(1); +} + +void +squeeze(int i) +{ + if(addr1 < zero+i || addr2 > dol || addr1 > addr2) + error(Q); +} + +void +newline(void) +{ + int c; + + c = getchr(); + if(c == '\n' || c == EOF) + return; + if(c == 'p' || c == 'l' || c == 'n') { + pflag++; + if(c == 'l') + listf++; + else + if(c == 'n') + listn++; + c = getchr(); + if(c == '\n') + return; + } + error(Q); +} + +void +filename(int comm) +{ + char *p1, *p2; + Rune rune; + int c; + + count = 0; + c = getchr(); + if(c == '\n' || c == EOF) { + p1 = savedfile; + if(*p1 == 0 && comm != 'f') + error(Q); + p2 = file; + while(*p2++ = *p1++) + ; + return; + } + if(c != ' ') + error(Q); + while((c=getchr()) == ' ') + ; + if(c == '\n') + error(Q); + p1 = file; + do { + if(p1 >= &file[sizeof(file)-6] || c == ' ' || c == EOF) + error(Q); + rune = c; + p1 += runetochar(p1, &rune); + } while((c=getchr()) != '\n'); + *p1 = 0; + if(savedfile[0] == 0 || comm == 'e' || comm == 'f') { + p1 = savedfile; + p2 = file; + while(*p1++ = *p2++) + ; + } +} + +void +exfile(int om) +{ + + if(om == OWRITE) + if(Bflush(&iobuf) < 0) + error(Q); + close(io); + io = -1; + if(vflag) { + putd(); + putchr(L'\n'); + } +} + +void +error1(char *s) +{ + int c; + + wrapp = 0; + listf = 0; + listn = 0; + count = 0; + seek(0, 0, 2); + pflag = 0; + if(globp) + lastc = '\n'; + globp = 0; + peekc = lastc; + if(lastc) + for(;;) { + c = getchr(); + if(c == '\n' || c == EOF) + break; + } + if(io > 0) { + close(io); + io = -1; + } + putchr(L'?'); + putst(s); +} + +void +error(char *s) +{ + error1(s); + longjmp(savej, 1); +} + +void +rescue(void) +{ + rescuing = 1; + if(dol > zero) { + addr1 = zero+1; + addr2 = dol; + io = create("ed.hup", OWRITE, 0666); + if(io > 0){ + Binit(&iobuf, io, OWRITE); + putfile(); + } + } + fchange = 0; + quit(); +} + +void +notifyf(void *a, char *s) +{ + if(strcmp(s, "interrupt") == 0){ + if(rescuing || waiting) + noted(NCONT); + putchr(L'\n'); + lastc = '\n'; + error1(Q); + notejmp(a, savej, 0); + } + if(strcmp(s, "hangup") == 0){ + if(rescuing) + noted(NDFLT); + rescue(); + } + fprint(2, "ed: note: %s\n", s); + abort(); +} + +int +getchr(void) +{ + char s[UTFmax]; + int i; + Rune r; + + if(lastc = peekc) { + peekc = 0; + return lastc; + } + if(globp) { + if((lastc=*globp++) != 0) + return lastc; + globp = 0; + return EOF; + } + for(i=0;;) { + if(read(0, s+i, 1) <= 0) + return lastc = EOF; + i++; + if(fullrune(s, i)) + break; + + } + chartorune(&r, s); + lastc = r; + return lastc; +} + +int +gety(void) +{ + int c; + Rune *gf, *p; + + p = linebuf; + gf = globp; + for(;;) { + c = getchr(); + if(c == '\n') { + *p = 0; + return 0; + } + if(c == EOF) { + if(gf) + peekc = c; + return c; + } + if(c == 0) + continue; + *p++ = c; + if(p >= &linebuf[LBSIZE-2]) + error(Q); + } + return 0; +} + +int +gettty(void) +{ + int rc; + + rc = gety(); + if(rc) + return rc; + if(linebuf[0] == '.' && linebuf[1] == 0) + return EOF; + return 0; +} + +int +getfile(void) +{ + int c; + Rune *lp; + + lp = linebuf; + do { + c = Bgetrune(&iobuf); + if(c < 0) { + if(lp > linebuf) { + putst("'\\n' appended"); + c = '\n'; + } else + return EOF; + } + if(lp >= &linebuf[LBSIZE]) { + lastc = '\n'; + error(Q); + } + *lp++ = c; + count++; + } while(c != '\n'); + lp[-1] = 0; + return 0; +} + +void +putfile(void) +{ + int *a1; + Rune *lp; + long c; + + a1 = addr1; + do { + lp = getline(*a1++); + for(;;) { + count++; + c = *lp++; + if(c == 0) { + if(Bputrune(&iobuf, '\n') < 0) + error(Q); + break; + } + if(Bputrune(&iobuf, c) < 0) + error(Q); + } + } while(a1 <= addr2); + if(Bflush(&iobuf) < 0) + error(Q); +} + +int +append(int (*f)(void), int *a) +{ + int *a1, *a2, *rdot, nline, tl; + + nline = 0; + dot = a; + while((*f)() == 0) { + if((dol-zero) >= nlall) { + nlall += 512; + a1 = realloc(zero, (nlall+5)*sizeof(int*)); + if(a1 == 0) { + error("MEM?"); + rescue(); + } + tl = a1 - zero; /* relocate pointers */ + zero += tl; + addr1 += tl; + addr2 += tl; + dol += tl; + dot += tl; + } + tl = putline(); + nline++; + a1 = ++dol; + a2 = a1+1; + rdot = ++dot; + while(a1 > rdot) + *--a2 = *--a1; + *rdot = tl; + } + return nline; +} + +void +add(int i) +{ + if(i && (given || dol > zero)) { + addr1--; + addr2--; + } + squeeze(0); + newline(); + append(gettty, addr2); +} + +void +browse(void) +{ + int forward, n; + static int bformat, bnum; /* 0 */ + + forward = 1; + peekc = getchr(); + if(peekc != '\n'){ + if(peekc == '-' || peekc == '+') { + if(peekc == '-') + forward = 0; + getchr(); + } + n = getnum(); + if(n > 0) + bpagesize = n; + } + newline(); + if(pflag) { + bformat = listf; + bnum = listn; + } else { + listf = bformat; + listn = bnum; + } + if(forward) { + addr1 = addr2; + addr2 += bpagesize; + if(addr2 > dol) + addr2 = dol; + } else { + addr1 = addr2-bpagesize; + if(addr1 <= zero) + addr1 = zero+1; + } + printcom(); +} + +void +callunix(void) +{ + int c, pid; + Rune rune; + char buf[512]; + char *p; + + setnoaddr(); + p = buf; + while((c=getchr()) != EOF && c != '\n') + if(p < &buf[sizeof(buf) - 6]) { + rune = c; + p += runetochar(p, &rune); + } + *p = 0; + pid = fork(); + if(pid == 0) { + execl("/bin/rc", "rc", "-c", buf, 0); + exits("execl failed"); + } + waiting = 1; + while(waitpid() != pid) + ; + waiting = 0; + if(vflag) + putst("!"); +} + +void +quit(void) +{ + if(vflag && fchange && dol!=zero) { + fchange = 0; + error(Q); + } + remove(tfname); + exits(0); +} + +void +onquit(int sig) +{ + USED(sig); + quit(); +} + +void +rdelete(int *ad1, int *ad2) +{ + int *a1, *a2, *a3; + + a1 = ad1; + a2 = ad2+1; + a3 = dol; + dol -= a2 - a1; + do { + *a1++ = *a2++; + } while(a2 <= a3); + a1 = ad1; + if(a1 > dol) + a1 = dol; + dot = a1; + fchange = 1; +} + +void +gdelete(void) +{ + int *a1, *a2, *a3; + + a3 = dol; + for(a1=zero; (*a1&01)==0; a1++) + if(a1>=a3) + return; + for(a2=a1+1; a2<=a3;) { + if(*a2 & 01) { + a2++; + dot = a1; + } else + *a1++ = *a2++; + } + dol = a1-1; + if(dot > dol) + dot = dol; + fchange = 1; +} + +Rune* +getline(int tl) +{ + Rune *lp, *bp; + int nl; + + lp = linebuf; + bp = getblock(tl, OREAD); + nl = nleft; + tl &= ~((BLKSIZE/2) - 1); + while(*lp++ = *bp++) { + nl -= sizeof(Rune); + if(nl == 0) { + bp = getblock(tl += BLKSIZE/2, OREAD); + nl = nleft; + } + } + return linebuf; +} + +int +putline(void) +{ + Rune *lp, *bp; + int nl, tl; + + fchange = 1; + lp = linebuf; + tl = tline; + bp = getblock(tl, OWRITE); + nl = nleft; + tl &= ~((BLKSIZE/2)-1); + while(*bp = *lp++) { + if(*bp++ == '\n') { + bp[-1] = 0; + linebp = lp; + break; + } + nl -= sizeof(Rune); + if(nl == 0) { + tl += BLKSIZE/2; + bp = getblock(tl, OWRITE); + nl = nleft; + } + } + nl = tline; + tline += ((lp-linebuf) + 03) & 077776; + return nl; +} + +void +blkio(int b, uchar *buf, int isread) +{ + int n; + + seek(tfile, b*BLKSIZE, 0); + if(isread) + n = read(tfile, buf, BLKSIZE); + else + n = write(tfile, buf, BLKSIZE); + if(n != BLKSIZE) + error(T); +} + +Rune* +getblock(int atl, int iof) +{ + int bno, off; + + static uchar ibuff[BLKSIZE]; + static uchar obuff[BLKSIZE]; + + bno = atl / (BLKSIZE/2); + off = (atl<<1) & (BLKSIZE-1) & ~03; + if(bno >= NBLK) { + lastc = '\n'; + error(T); + } + nleft = BLKSIZE - off; + if(bno == iblock) { + ichanged |= iof; + return (Rune*)(ibuff+off); + } + if(bno == oblock) + return (Rune*)(obuff+off); + if(iof == OREAD) { + if(ichanged) + blkio(iblock, ibuff, 0); + ichanged = 0; + iblock = bno; + blkio(bno, ibuff, 1); + return (Rune*)(ibuff+off); + } + if(oblock >= 0) + blkio(oblock, obuff, 0); + oblock = bno; + return (Rune*)(obuff+off); +} + +void +init(void) +{ + int *markp; + + close(tfile); + tline = 2; + for(markp = names; markp < &names[26]; ) + *markp++ = 0; + subnewa = 0; + anymarks = 0; + iblock = -1; + oblock = -1; + ichanged = 0; + if((tfile = create(tfname, ORDWR, 0600)) < 0){ + error1(T); + exits(0); + } + dot = dol = zero; +} + +void +global(int k) +{ + Rune *gp, globuf[GBSIZE]; + int c, *a1; + + if(globp) + error(Q); + setwide(); + squeeze(dol > zero); + c = getchr(); + if(c == '\n') + error(Q); + compile(c); + gp = globuf; + while((c=getchr()) != '\n') { + if(c == EOF) + error(Q); + if(c == '\\') { + c = getchr(); + if(c != '\n') + *gp++ = '\\'; + } + *gp++ = c; + if(gp >= &globuf[GBSIZE-2]) + error(Q); + } + if(gp == globuf) + *gp++ = 'p'; + *gp++ = '\n'; + *gp = 0; + for(a1=zero; a1<=dol; a1++) { + *a1 &= ~01; + if(a1 >= addr1 && a1 <= addr2 && match(a1) == k) + *a1 |= 01; + } + + /* + * Special case: g/.../d (avoid n^2 algorithm) + */ + if(globuf[0] == 'd' && globuf[1] == '\n' && globuf[2] == 0) { + gdelete(); + return; + } + for(a1=zero; a1<=dol; a1++) { + if(*a1 & 01) { + *a1 &= ~01; + dot = a1; + globp = globuf; + commands(); + a1 = zero; + } + } +} + +void +join(void) +{ + Rune *gp, *lp; + int *a1; + + nonzero(); + gp = genbuf; + for(a1=addr1; a1<=addr2; a1++) { + lp = getline(*a1); + while(*gp = *lp++) + if(gp++ >= &genbuf[LBSIZE-2]) + error(Q); + } + lp = linebuf; + gp = genbuf; + while(*lp++ = *gp++) + ; + *addr1 = putline(); + if(addr1 < addr2) + rdelete(addr1+1, addr2); + dot = addr1; +} + +void +substitute(int inglob) +{ + int *mp, *a1, nl, gsubf, n; + + n = getnum(); /* OK even if n==0 */ + gsubf = compsub(); + for(a1 = addr1; a1 <= addr2; a1++) { + if(match(a1)){ + int *ozero; + int m = n; + + do { + int span = loc2-loc1; + + if(--m <= 0) { + dosub(); + if(!gsubf) + break; + if(span == 0) { /* null RE match */ + if(*loc2 == 0) + break; + loc2++; + } + } + } while(match(0)); + if(m <= 0) { + inglob |= 01; + subnewa = putline(); + *a1 &= ~01; + if(anymarks) { + for(mp=names; mp<&names[26]; mp++) + if(*mp == *a1) + *mp = subnewa; + } + subolda = *a1; + *a1 = subnewa; + ozero = zero; + nl = append(getsub, a1); + addr2 += nl; + nl += zero-ozero; + a1 += nl; + } + } + } + if(inglob == 0) + error(Q); +} + +int +compsub(void) +{ + int seof, c; + Rune *p; + + seof = getchr(); + if(seof == '\n' || seof == ' ') + error(Q); + compile(seof); + p = rhsbuf; + for(;;) { + c = getchr(); + if(c == '\\') { + c = getchr(); + *p++ = ESCFLG; + if(p >= &rhsbuf[LBSIZE/2]) + error(Q); + } else + if(c == '\n' && (!globp || !globp[0])) { + peekc = c; + pflag++; + break; + } else + if(c == seof) + break; + *p++ = c; + if(p >= &rhsbuf[LBSIZE/2]) + error(Q); + } + *p = 0; + peekc = getchr(); + if(peekc == 'g') { + peekc = 0; + newline(); + return 1; + } + newline(); + return 0; +} + +int +getsub(void) +{ + Rune *p1, *p2; + + p1 = linebuf; + if((p2 = linebp) == 0) + return EOF; + while(*p1++ = *p2++) + ; + linebp = 0; + return 0; +} + +void +dosub(void) +{ + Rune *lp, *sp, *rp; + int c, n; + + lp = linebuf; + sp = genbuf; + rp = rhsbuf; + while(lp < loc1) + *sp++ = *lp++; + while(c = *rp++) { + if(c == '&'){ + sp = place(sp, loc1, loc2); + continue; + } + if(c == ESCFLG && (c = *rp++) >= '1' && c < MAXSUB+'0') { + n = c-'0'; + if(subexp[n].s.rsp && subexp[n].e.rep) { + sp = place(sp, subexp[n].s.rsp, subexp[n].e.rep); + continue; + } + error(Q); + } + *sp++ = c; + if(sp >= &genbuf[LBSIZE]) + error(Q); + } + lp = loc2; + loc2 = sp - genbuf + linebuf; + while(*sp++ = *lp++) + if(sp >= &genbuf[LBSIZE]) + error(Q); + lp = linebuf; + sp = genbuf; + while(*lp++ = *sp++) + ; +} + +Rune* +place(Rune *sp, Rune *l1, Rune *l2) +{ + + while(l1 < l2) { + *sp++ = *l1++; + if(sp >= &genbuf[LBSIZE]) + error(Q); + } + return sp; +} + +void +move(int cflag) +{ + int *adt, *ad1, *ad2; + + nonzero(); + if((adt = address())==0) /* address() guarantees addr is in range */ + error(Q); + newline(); + if(cflag) { + int *ozero, delta; + ad1 = dol; + ozero = zero; + append(getcopy, ad1++); + ad2 = dol; + delta = zero - ozero; + ad1 += delta; + adt += delta; + } else { + ad2 = addr2; + for(ad1 = addr1; ad1 <= ad2;) + *ad1++ &= ~01; + ad1 = addr1; + } + ad2++; + if(adt<ad1) { + dot = adt + (ad2-ad1); + if((++adt)==ad1) + return; + reverse(adt, ad1); + reverse(ad1, ad2); + reverse(adt, ad2); + } else + if(adt >= ad2) { + dot = adt++; + reverse(ad1, ad2); + reverse(ad2, adt); + reverse(ad1, adt); + } else + error(Q); + fchange = 1; +} + +void +reverse(int *a1, int *a2) +{ + int t; + + for(;;) { + t = *--a2; + if(a2 <= a1) + return; + *a2 = *a1; + *a1++ = t; + } +} + +int +getcopy(void) +{ + if(addr1 > addr2) + return EOF; + getline(*addr1++); + return 0; +} + +void +compile(int eof) +{ + Rune c; + char *ep; + char expbuf[ESIZE]; + + if((c = getchr()) == '\n') { + peekc = c; + c = eof; + } + if(c == eof) { + if(!pattern) + error(Q); + return; + } + if(pattern) { + free(pattern); + pattern = 0; + } + ep = expbuf; + do { + if(c == '\\') { + if(ep >= expbuf+sizeof(expbuf)) { + error(Q); + return; + } + ep += runetochar(ep, &c); + if((c = getchr()) == '\n') { + error(Q); + return; + } + } + if(ep >= expbuf+sizeof(expbuf)) { + error(Q); + return; + } + ep += runetochar(ep, &c); + } while((c = getchr()) != eof && c != '\n'); + if(c == '\n') + peekc = c; + *ep = 0; + pattern = regcomp(expbuf); +} + +int +match(int *addr) +{ + if(!pattern) + return 0; + if(addr){ + if(addr == zero) + return 0; + subexp[0].s.rsp = getline(*addr); + } else + subexp[0].s.rsp = loc2; + subexp[0].e.rep = 0; + if(rregexec(pattern, linebuf, subexp, MAXSUB)) { + loc1 = subexp[0].s.rsp; + loc2 = subexp[0].e.rep; + return 1; + } + loc1 = loc2 = 0; + return 0; + +} + +void +putd(void) +{ + int r; + + r = count%10; + count /= 10; + if(count) + putd(); + putchr(r + L'0'); +} + +void +putst(char *sp) +{ + Rune r; + + col = 0; + for(;;) { + sp += chartorune(&r, sp); + if(r == 0) + break; + putchr(r); + } + putchr(L'\n'); +} + +void +putshst(Rune *sp) +{ + col = 0; + while(*sp) + putchr(*sp++); + putchr(L'\n'); +} + +void +putchr(int ac) +{ + char *lp; + int c; + Rune rune; + + lp = linp; + c = ac; + if(listf) { + if(c == '\n') { + if(linp != line && linp[-1] == ' ') { + *lp++ = '\\'; + *lp++ = 'n'; + } + } else { + if(col > (72-6-2)) { + col = 8; + *lp++ = '\\'; + *lp++ = '\n'; + *lp++ = '\t'; + } + col++; + if(c=='\b' || c=='\t' || c=='\\') { + *lp++ = '\\'; + if(c == '\b') + c = 'b'; + else + if(c == '\t') + c = 't'; + col++; + } else + if(c<' ' || c>='\177') { + *lp++ = '\\'; + *lp++ = 'x'; + *lp++ = hex[c>>12]; + *lp++ = hex[c>>8&0xF]; + *lp++ = hex[c>>4&0xF]; + c = hex[c&0xF]; + col += 5; + } + } + } + + rune = c; + lp += runetochar(lp, &rune); + + if(c == '\n' || lp >= &line[sizeof(line)-5]) { + linp = line; + write(oflag? 2: 1, line, lp-line); + return; + } + linp = lp; +} + +char* +mktemp(char *as) +{ + char *s; + unsigned pid; + int i; + + pid = getpid(); + s = as; + while(*s++) + ; + s--; + while(*--s == 'X') { + *s = pid % 10 + '0'; + pid /= 10; + } + s++; + i = 'a'; + while(access(as, 0) != -1) { + if(i == 'z') + return "/"; + *s = i++; + } + return as; +} + +void +regerror(char *s) +{ + USED(s); + error(Q); +} diff --git a/src/cmd/factor.c b/src/cmd/factor.c new file mode 100644 index 00000000..0232d4e5 --- /dev/null +++ b/src/cmd/factor.c @@ -0,0 +1,96 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +#define whsiz (sizeof(wheel)/sizeof(wheel[0])) + +double wheel[] = +{ + 2,10, 2, 4, 2, 4, 6, 2, 6, 4, + 2, 4, 6, 6, 2, 6, 4, 2, 6, 4, + 6, 8, 4, 2, 4, 2, 4, 8, 6, 4, + 6, 2, 4, 6, 2, 6, 6, 4, 2, 4, + 6, 2, 6, 4, 2, 4, 2,10, +}; + +Biobuf bin; + +void factor(double); + +void +main(int argc, char *argv[]) +{ + double n; + int i; + char *l; + + if(argc > 1) { + for(i=1; i<argc; i++) { + n = atof(argv[i]); + factor(n); + } + exits(0); + } + + Binit(&bin, 0, OREAD); + for(;;) { + l = Brdline(&bin, '\n'); + if(l == 0) + break; + n = atof(l); + if(n <= 0) + break; + factor(n); + } + exits(0); +} + +void +factor(double n) +{ + double quot, d, s; + int i; + + print("%.0f\n", n); + if(n == 0) + return; + s = sqrt(n) + 1; + while(modf(n/2, ") == 0) { + print(" 2\n"); + n = quot; + s = sqrt(n) + 1; + } + while(modf(n/3, ") == 0) { + print(" 3\n"); + n = quot; + s = sqrt(n) + 1; + } + while(modf(n/5, ") == 0) { + print(" 5\n"); + n = quot; + s = sqrt(n) + 1; + } + while(modf(n/7, ") == 0) { + print(" 7\n"); + n = quot; + s = sqrt(n) + 1; + } + d = 1; + for(i=1;;) { + d += wheel[i]; + while(modf(n/d, ") == 0) { + print(" %.0f\n", d); + n = quot; + s = sqrt(n) + 1; + } + i++; + if(i >= whsiz) { + i = 0; + if(d > s) + break; + } + } + if(n > 1) + print(" %.0f\n",n); + print("\n"); +} diff --git a/src/cmd/freq.c b/src/cmd/freq.c new file mode 100644 index 00000000..dfc1e8cf --- /dev/null +++ b/src/cmd/freq.c @@ -0,0 +1,111 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +long count[1<<16]; +Biobuf bout; + +void freq(int, char*); +long flag; +enum +{ + Fdec = 1<<0, + Fhex = 1<<1, + Foct = 1<<2, + Fchar = 1<<3, + Frune = 1<<4, +}; + +void +main(int argc, char *argv[]) +{ + int f, i; + + flag = 0; + Binit(&bout, 1, OWRITE); + ARGBEGIN{ + default: + fprint(2, "freq: unknown option %c\n", ARGC()); + exits("usage"); + case 'd': + flag |= Fdec; + break; + case 'x': + flag |= Fhex; + break; + case 'o': + flag |= Foct; + break; + case 'c': + flag |= Fchar; + break; + case 'r': + flag |= Frune; + break; + }ARGEND + if((flag&(Fdec|Fhex|Foct|Fchar)) == 0) + flag |= Fdec | Fhex | Foct | Fchar; + if(argc < 1) { + freq(0, "-"); + exits(0); + } + for(i=0; i<argc; i++) { + f = open(argv[i], 0); + if(f < 0) { + fprint(2, "cannot open %s\n", argv[i]); + continue; + } + freq(f, argv[i]); + close(f); + } + exits(0); +} + +void +freq(int f, char *s) +{ + Biobuf bin; + long c, i; + + memset(count, 0, sizeof(count)); + Binit(&bin, f, OREAD); + if(flag & Frune) { + for(;;) { + c = Bgetrune(&bin); + if(c < 0) + break; + count[c]++; + } + } else { + for(;;) { + c = Bgetc(&bin); + if(c < 0) + break; + count[c]++; + } + } + Bterm(&bin); + if(c != Beof) + fprint(2, "freq: read error on %s\n", s); + + for(i=0; i<nelem(count); i++) { + if(count[i] == 0) + continue; + if(flag & Fdec) + Bprint(&bout, "%3ld ", i); + if(flag & Foct) + Bprint(&bout, "%.3lo ", i); + if(flag & Fhex) + Bprint(&bout, "%.2lx ", i); + if(flag & Fchar) { + if(i <= 0x20 || + i >= 0x7f && i < 0xa0 || + i > 0xff && !(flag & Frune)) + Bprint(&bout, "- "); + else + Bprint(&bout, "%C ", (int)i); + } + Bprint(&bout, "%8ld\n", count[i]); + } + Bflush(&bout); +} diff --git a/src/cmd/fsize.c b/src/cmd/fsize.c new file mode 100644 index 00000000..e8d73075 --- /dev/null +++ b/src/cmd/fsize.c @@ -0,0 +1,32 @@ +#include <u.h> +#include <libc.h> + +void +usage(void) +{ + fprint(2, "usage: fsize file...\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int i; + Dir *d; + + ARGBEGIN{ + default: + usage(); + }ARGEND + if(argc == 0) + usage(); + + for(i=0; i<argc; i++){ + if((d = dirstat(argv[i])) == nil) + fprint(2, "dirstat %s: %r", argv[i]); + else{ + print("%s: %lld\n", argv[i], d->length); + free(d); + } + } +} diff --git a/src/cmd/idiff.c b/src/cmd/idiff.c new file mode 100644 index 00000000..47326782 --- /dev/null +++ b/src/cmd/idiff.c @@ -0,0 +1,335 @@ +/* + * interactive diff, inspired/stolen from + * kernighan and pike, _unix programming environment_. + */ + +#include <u.h> +#include <libc.h> +#include <bio.h> + +int diffbflag; +int diffwflag; + +void copy(Biobuf*, char*, Biobuf*, char*); +void idiff(Biobuf*, char*, Biobuf*, char*, Biobuf*, char*, Biobuf*, char*); +int opentemp(char*, int, long); +void rundiff(char*, char*, int); + +void +usage(void) +{ + fprint(2, "usage: idiff [-bw] file1 file2\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int fd, ofd; + char diffout[40], idiffout[40]; + Biobuf *b1, *b2, bdiff, bout, bstdout; + Dir *d; + + ARGBEGIN{ + default: + usage(); + case 'b': + diffbflag++; + break; + case 'w': + diffwflag++; + break; + }ARGEND + + if(argc != 2) + usage(); + + if((d = dirstat(argv[0])) == nil) + sysfatal("stat %s: %r", argv[0]); + if(d->mode&DMDIR) + sysfatal("%s is a directory", argv[0]); + free(d); + if((d = dirstat(argv[1])) == nil) + sysfatal("stat %s: %r", argv[1]); + if(d->mode&DMDIR) + sysfatal("%s is a directory", argv[1]); + free(d); + + if((b1 = Bopen(argv[0], OREAD)) == nil) + sysfatal("open %s: %r", argv[0]); + if((b2 = Bopen(argv[1], OREAD)) == nil) + sysfatal("open %s: %r", argv[1]); + + strcpy(diffout, "/tmp/idiff.XXXXXX"); + fd = opentemp(diffout, ORDWR|ORCLOSE, 0); + strcpy(idiffout, "/tmp/idiff.XXXXXX"); + ofd = opentemp(idiffout, ORDWR|ORCLOSE, 0); + rundiff(argv[0], argv[1], fd); + seek(fd, 0, 0); + Binit(&bdiff, fd, OREAD); + Binit(&bout, ofd, OWRITE); + idiff(b1, argv[0], b2, argv[1], &bdiff, diffout, &bout, idiffout); + Bterm(&bdiff); + Bflush(&bout); + seek(ofd, 0, 0); + Binit(&bout, ofd, OREAD); + Binit(&bstdout, 1, OWRITE); + copy(&bout, idiffout, &bstdout, "<stdout>"); + exits(nil); +} + +int +opentemp(char *template, int mode, long perm) +{ + int fd, i; + char *p; + + p = strdup(template); + if(p == nil) + sysfatal("strdup out of memory"); + fd = -1; + for(i=0; i<10; i++){ + mktemp(p); + if(access(p, 0) < 0 && (fd=create(p, mode, perm)) >= 0) + break; + strcpy(p, template); + } + if(fd < 0) + sysfatal("could not create temporary file"); + strcpy(template, p); + free(p); + + return fd; +} + +void +rundiff(char *arg1, char *arg2, int outfd) +{ + char *arg[10], *p; + int narg, pid; + Waitmsg *w; + + narg = 0; + arg[narg++] = "/bin/diff"; + arg[narg++] = "-n"; + if(diffbflag) + arg[narg++] = "-b"; + if(diffwflag) + arg[narg++] = "-w"; + arg[narg++] = arg1; + arg[narg++] = arg2; + arg[narg] = nil; + + switch(pid = fork()){ + case -1: + sysfatal("fork: %r"); + + case 0: + dup(outfd, 1); + close(0); + exec("/bin/diff", arg); + sysfatal("exec: %r"); + + default: + w = wait(); + if(w==nil) + sysfatal("wait: %r"); + if(w->pid != pid) + sysfatal("wait got unexpected pid %d", w->pid); + if((p = strchr(w->msg, ':')) && strcmp(p, ": some") != 0) + sysfatal("%s", w->msg); + free(w); + } +} + +void +runcmd(char *cmd) +{ + char *arg[10]; + int narg, pid, wpid; + + narg = 0; + arg[narg++] = "/bin/rc"; + arg[narg++] = "-c"; + arg[narg++] = cmd; + arg[narg] = nil; + + switch(pid = fork()){ + case -1: + sysfatal("fork: %r"); + + case 0: + exec("/bin/rc", arg); + sysfatal("exec: %r"); + + default: + wpid = waitpid(); + if(wpid < 0) + sysfatal("wait: %r"); + if(wpid != pid) + sysfatal("wait got unexpected pid %d", wpid); + } +} + +void +parse(char *s, int *pfrom1, int *pto1, int *pcmd, int *pfrom2, int *pto2) +{ + *pfrom1 = *pto1 = *pfrom2 = *pto2 = 0; + + s = strchr(s, ':'); + if(s == nil) + sysfatal("bad diff output0"); + s++; + *pfrom1 = strtol(s, &s, 10); + if(*s == ','){ + s++; + *pto1 = strtol(s, &s, 10); + }else + *pto1 = *pfrom1; + if(*s++ != ' ') + sysfatal("bad diff output1"); + *pcmd = *s++; + if(*s++ != ' ') + sysfatal("bad diff output2"); + s = strchr(s, ':'); + if(s == nil) + sysfatal("bad diff output3"); + s++; + *pfrom2 = strtol(s, &s, 10); + if(*s == ','){ + s++; + *pto2 = strtol(s, &s, 10); + }else + *pto2 = *pfrom2; +} + +void +skiplines(Biobuf *b, char *name, int n) +{ + int i; + + for(i=0; i<n; i++){ + while(Brdline(b, '\n')==nil){ + if(Blinelen(b) <= 0) + sysfatal("early end of file on %s", name); + Bseek(b, Blinelen(b), 1); + } + } +} + +void +copylines(Biobuf *bin, char *nin, Biobuf *bout, char *nout, int n) +{ + char buf[4096], *p; + int i, m; + + for(i=0; i<n; i++){ + while((p=Brdline(bin, '\n'))==nil){ + if(Blinelen(bin) <= 0) + sysfatal("early end of file on %s", nin); + m = Blinelen(bin); + if(m > sizeof buf) + m = sizeof buf; + m = Bread(bin, buf, m); + if(Bwrite(bout, buf, m) != m) + sysfatal("error writing %s: %r", nout); + } + if(Bwrite(bout, p, Blinelen(bin)) != Blinelen(bin)) + sysfatal("error writing %s: %r", nout); + } +} + +void +copy(Biobuf *bin, char *nin, Biobuf *bout, char *nout) +{ + char buf[4096]; + int m; + + USED(nin); + while((m = Bread(bin, buf, sizeof buf)) > 0) + if(Bwrite(bout, buf, m) != m) + sysfatal("error writing %s: %r", nout); +} + +void +idiff(Biobuf *b1, char *name1, Biobuf *b2, char *name2, Biobuf *bdiff, char *namediff, Biobuf *bout, char *nameout) +{ + char buf[256], *p; + int interactive, defaultanswer, cmd, diffoffset; + int n, from1, to1, from2, to2, nf1, nf2; + Biobuf berr; + + nf1 = 1; + nf2 = 1; + interactive = 1; + defaultanswer = 0; + Binit(&berr, 2, OWRITE); + while(diffoffset = Boffset(bdiff), p = Brdline(bdiff, '\n')){ + p[Blinelen(bdiff)-1] = '\0'; + parse(p, &from1, &to1, &cmd, &from2, &to2); + p[Blinelen(bdiff)-1] = '\n'; + n = to1-from1 + to2-from2 + 1; /* #lines from diff */ + if(cmd == 'c') + n += 2; + else if(cmd == 'a') + from1++; + else if(cmd == 'd') + from2++; + to1++; /* make half-open intervals */ + to2++; + if(interactive){ + p[Blinelen(bdiff)-1] = '\0'; + fprint(2, "%s\n", p); + p[Blinelen(bdiff)-1] = '\n'; + copylines(bdiff, namediff, &berr, "<stderr>", n); + Bflush(&berr); + }else + skiplines(bdiff, namediff, n); + do{ + if(interactive){ + fprint(2, "? "); + memset(buf, 0, sizeof buf); + if(read(0, buf, sizeof buf - 1) < 0) + sysfatal("read console: %r"); + }else + buf[0] = defaultanswer; + + switch(buf[0]){ + case '>': + copylines(b1, name1, bout, nameout, from1-nf1); + skiplines(b1, name1, to1-from1); + skiplines(b2, name2, from2-nf2); + copylines(b2, name2, bout, nameout, to2-from2); + break; + case '<': + copylines(b1, name1, bout, nameout, to1-nf1); + skiplines(b2, name2, to2-nf2); + break; + case '=': + copylines(b1, name1, bout, nameout, from1-nf1); + skiplines(b1, name1, to1-from1); + skiplines(b2, name2, to2-nf2); + if(Bseek(bdiff, diffoffset, 0) != diffoffset) + sysfatal("seek in diff output: %r"); + copylines(bdiff, namediff, bout, nameout, n+1); + break; + case '!': + runcmd(buf+1); + break; + case 'q': + if(buf[1]=='<' || buf[1]=='>' || buf[1]=='='){ + interactive = 0; + defaultanswer = buf[1]; + }else + fprint(2, "must be q<, q>, or q=\n"); + break; + default: + fprint(2, "expect: <, >, =, q<, q>, q=, !cmd\n"); + break; + } + }while(buf[0] != '<' && buf[0] != '>' && buf[0] != '='); + nf1 = to1; + nf2 = to2; + } + copy(b1, name1, bout, nameout); +} diff --git a/src/cmd/join.c b/src/cmd/join.c new file mode 100644 index 00000000..7efff976 --- /dev/null +++ b/src/cmd/join.c @@ -0,0 +1,369 @@ +/* join F1 F2 on stuff */ +#include <u.h> +#include <libc.h> +#include <stdio.h> +#include <ctype.h> +#define F1 0 +#define F2 1 +#define F0 3 +#define NFLD 100 /* max field per line */ +#define comp() runecmp(ppi[F1][j1],ppi[F2][j2]) +FILE *f[2]; +Rune buf[2][BUFSIZ]; /*input lines */ +Rune *ppi[2][NFLD+1]; /* pointers to fields in lines */ +Rune *s1,*s2; +#define j1 joinj1 +#define j2 joinj2 + +int j1 = 1; /* join of this field of file 1 */ +int j2 = 1; /* join of this field of file 2 */ +int olist[2*NFLD]; /* output these fields */ +int olistf[2*NFLD]; /* from these files */ +int no; /* number of entries in olist */ +Rune sep1 = ' '; /* default field separator */ +Rune sep2 = '\t'; +char *sepstr=" "; +int discard; /* count of truncated lines */ +Rune null[BUFSIZ]/* = L""*/; +int a1; +int a2; + +char *getoptarg(int*, char***); +void output(int, int); +int input(int); +void oparse(char*); +void error(char*, char*); +void seek1(void), seek2(void); +Rune *strtorune(Rune *, char *); + + +void +main(int argc, char **argv) +{ + int i; + + while (argc > 1 && argv[1][0] == '-') { + if (argv[1][1] == '\0') + break; + switch (argv[1][1]) { + case '-': + argc--; + argv++; + goto proceed; + case 'a': + switch(*getoptarg(&argc, &argv)) { + case '1': + a1++; + break; + case '2': + a2++; + break; + default: + error("incomplete option -a",""); + } + break; + case 'e': + strtorune(null, getoptarg(&argc, &argv)); + break; + case 't': + sepstr=getoptarg(&argc, &argv); + chartorune(&sep1, sepstr); + sep2 = sep1; + break; + case 'o': + if(argv[1][2]!=0 || + argc>2 && strchr(argv[2],',')!=0) + oparse(getoptarg(&argc, &argv)); + else for (no = 0; no<2*NFLD && argc>2; no++){ + if (argv[2][0] == '1' && argv[2][1] == '.') { + olistf[no] = F1; + olist[no] = atoi(&argv[2][2]); + } else if (argv[2][0] == '2' && argv[2][1] == '.') { + olist[no] = atoi(&argv[2][2]); + olistf[no] = F2; + } else if (argv[2][0] == '0') + olistf[no] = F0; + else + break; + argc--; + argv++; + } + break; + case 'j': + if(argc <= 2) + break; + if (argv[1][2] == '1') + j1 = atoi(argv[2]); + else if (argv[1][2] == '2') + j2 = atoi(argv[2]); + else + j1 = j2 = atoi(argv[2]); + argc--; + argv++; + break; + case '1': + j1 = atoi(getoptarg(&argc, &argv)); + break; + case '2': + j2 = atoi(getoptarg(&argc, &argv)); + break; + } + argc--; + argv++; + } +proceed: + for (i = 0; i < no; i++) + if (olist[i]-- > NFLD) /* 0 origin */ + error("field number too big in -o",""); + if (argc != 3) + error("usage: join [-1 x -2 y] [-o list] file1 file2",""); + j1--; + j2--; /* everyone else believes in 0 origin */ + s1 = ppi[F1][j1]; + s2 = ppi[F2][j2]; + if (strcmp(argv[1], "-") == 0) + f[F1] = stdin; + else if ((f[F1] = fopen(argv[1], "r")) == 0) + error("can't open %s", argv[1]); + if(strcmp(argv[2], "-") == 0) { + f[F2] = stdin; + } else if ((f[F2] = fopen(argv[2], "r")) == 0) + error("can't open %s", argv[2]); + + if(ftell(f[F2]) >= 0) + seek2(); + else if(ftell(f[F1]) >= 0) + seek1(); + else + error("neither file is randomly accessible",""); + if (discard) + error("some input line was truncated", ""); + exits(""); +} +int runecmp(Rune *a, Rune *b){ + while(*a==*b){ + if(*a=='\0') return 0; + a++; + b++; + } + if(*a<*b) return -1; + return 1; +} +char *runetostr(char *buf, Rune *r){ + char *s; + for(s=buf;*r;r++) s+=runetochar(s, r); + *s='\0'; + return buf; +} +Rune *strtorune(Rune *buf, char *s){ + Rune *r; + for(r=buf;*s;r++) s+=chartorune(r, s); + *r='\0'; + return buf; +} +/* lazy. there ought to be a clean way to combine seek1 & seek2 */ +#define get1() n1=input(F1) +#define get2() n2=input(F2) +void +seek2() +{ + int n1, n2; + int top2=0; + int bot2 = ftell(f[F2]); + get1(); + get2(); + while(n1>0 && n2>0 || (a1||a2) && n1+n2>0) { + if(n1>0 && n2>0 && comp()>0 || n1==0) { + if(a2) output(0, n2); + bot2 = ftell(f[F2]); + get2(); + } else if(n1>0 && n2>0 && comp()<0 || n2==0) { + if(a1) output(n1, 0); + get1(); + } else /*(n1>0 && n2>0 && comp()==0)*/ { + while(n2>0 && comp()==0) { + output(n1, n2); + top2 = ftell(f[F2]); + get2(); + } + fseek(f[F2], bot2, 0); + get2(); + get1(); + for(;;) { + if(n1>0 && n2>0 && comp()==0) { + output(n1, n2); + get2(); + } else if(n1>0 && n2>0 && comp()<0 || n2==0) { + fseek(f[F2], bot2, 0); + get2(); + get1(); + } else /*(n1>0 && n2>0 && comp()>0 || n1==0)*/{ + fseek(f[F2], top2, 0); + bot2 = top2; + get2(); + break; + } + } + } + } +} +void +seek1() +{ + int n1, n2; + int top1=0; + int bot1 = ftell(f[F1]); + get1(); + get2(); + while(n1>0 && n2>0 || (a1||a2) && n1+n2>0) { + if(n1>0 && n2>0 && comp()>0 || n1==0) { + if(a2) output(0, n2); + get2(); + } else if(n1>0 && n2>0 && comp()<0 || n2==0) { + if(a1) output(n1, 0); + bot1 = ftell(f[F1]); + get1(); + } else /*(n1>0 && n2>0 && comp()==0)*/ { + while(n2>0 && comp()==0) { + output(n1, n2); + top1 = ftell(f[F1]); + get1(); + } + fseek(f[F1], bot1, 0); + get2(); + get1(); + for(;;) { + if(n1>0 && n2>0 && comp()==0) { + output(n1, n2); + get1(); + } else if(n1>0 && n2>0 && comp()>0 || n1==0) { + fseek(f[F1], bot1, 0); + get2(); + get1(); + } else /*(n1>0 && n2>0 && comp()<0 || n2==0)*/{ + fseek(f[F1], top1, 0); + bot1 = top1; + get1(); + break; + } + } + } + } +} + +int +input(int n) /* get input line and split into fields */ +{ + register int i, c; + Rune *bp; + Rune **pp; + char line[BUFSIZ]; + + bp = buf[n]; + pp = ppi[n]; + if (fgets(line, BUFSIZ, f[n]) == 0) + return(0); + strtorune(bp, line); + i = 0; + do { + i++; + if (sep1 == ' ') /* strip multiples */ + while ((c = *bp) == sep1 || c == sep2) + bp++; /* skip blanks */ + *pp++ = bp; /* record beginning */ + while ((c = *bp) != sep1 && c != '\n' && c != sep2 && c != '\0') + bp++; + *bp++ = '\0'; /* mark end by overwriting blank */ + } while (c != '\n' && c != '\0' && i < NFLD-1); + if (c != '\n') + discard++; + + *pp = 0; + return(i); +} + +void +output(int on1, int on2) /* print items from olist */ +{ + int i; + Rune *temp; + char buf[BUFSIZ]; + + if (no <= 0) { /* default case */ + printf("%s", runetostr(buf, on1? ppi[F1][j1]: ppi[F2][j2])); + for (i = 0; i < on1; i++) + if (i != j1) + printf("%s%s", sepstr, runetostr(buf, ppi[F1][i])); + for (i = 0; i < on2; i++) + if (i != j2) + printf("%s%s", sepstr, runetostr(buf, ppi[F2][i])); + printf("\n"); + } else { + for (i = 0; i < no; i++) { + if (olistf[i]==F0 && on1>j1) + temp = ppi[F1][j1]; + else if (olistf[i]==F0 && on2>j2) + temp = ppi[F2][j2]; + else { + temp = ppi[olistf[i]][olist[i]]; + if(olistf[i]==F1 && on1<=olist[i] || + olistf[i]==F2 && on2<=olist[i] || + *temp==0) + temp = null; + } + printf("%s", runetostr(buf, temp)); + if (i == no - 1) + printf("\n"); + else + printf("%s", sepstr); + } + } +} + +void +error(char *s1, char *s2) +{ + fprintf(stderr, "join: "); + fprintf(stderr, s1, s2); + fprintf(stderr, "\n"); + exits(s1); +} + +char * +getoptarg(int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + if(argv[1][2] != 0) + return &argv[1][2]; + if(argc<=2 || argv[2][0]=='-') + error("incomplete option %s", argv[1]); + *argcp = argc-1; + *argvp = ++argv; + return argv[1]; +} + +void +oparse(char *s) +{ + for (no = 0; no<2*NFLD && *s; no++, s++) { + switch(*s) { + case 0: + return; + case '0': + olistf[no] = F0; + break; + case '1': + case '2': + if(s[1] == '.' && isdigit(s[2])) { + olistf[no] = *s=='1'? F1: F2; + olist[no] = atoi(s += 2); + break; + } /* fall thru */ + default: + error("invalid -o list", ""); + } + if(s[1] == ',') + s++; + } +} diff --git a/src/cmd/ls.C b/src/cmd/ls.C new file mode 100644 index 00000000..de2bee94 --- /dev/null +++ b/src/cmd/ls.C @@ -0,0 +1,305 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +typedef struct NDir NDir; +struct NDir +{ + Dir *d; + char *prefix; +}; + +int errs = 0; +int dflag; +int lflag; +int mflag; +int nflag; +int pflag; +int qflag; +int Qflag; +int rflag; +int sflag; +int tflag; +int uflag; +int Fflag; +int ndirbuf; +int ndir; +NDir* dirbuf; +int ls(char*, int); +int compar(NDir*, NDir*); +char* asciitime(long); +char* darwx(long); +void rwx(long, char*); +void growto(long); +void dowidths(Dir*); +void format(Dir*, char*); +void output(void); +ulong clk; +int swidth; /* max width of -s size */ +int qwidth; /* max width of -q version */ +int vwidth; /* max width of dev */ +int uwidth; /* max width of userid */ +int mwidth; /* max width of muid */ +int glwidth; /* max width of groupid and length */ +Biobuf bin; + +void +main(int argc, char *argv[]) +{ + int i; + + Binit(&bin, 1, OWRITE); + ARGBEGIN{ + case 'F': Fflag++; break; + case 'd': dflag++; break; + case 'l': lflag++; break; + case 'm': mflag++; break; + case 'n': nflag++; break; + case 'p': pflag++; break; + case 'q': qflag++; break; + case 'Q': Qflag++; break; + case 'r': rflag++; break; + case 's': sflag++; break; + case 't': tflag++; break; + case 'u': uflag++; break; + default: fprint(2, "usage: ls [-dlmnpqrstuFQ] [file ...]\n"); + exits("usage"); + }ARGEND + + doquote = needsrcquote; + quotefmtinstall(); + fmtinstall('M', dirmodefmt); + + if(lflag) + clk = time(0); + if(argc == 0) + errs = ls(".", 0); + else for(i=0; i<argc; i++) + errs |= ls(argv[i], 1); + output(); + exits(errs? "errors" : 0); +} + +int +ls(char *s, int multi) +{ + int fd; + long i, n; + char *p; + Dir *db; + + for(;;) { + p = utfrrune(s, '/'); + if(p == 0 || p[1] != 0 || p == s) + break; + *p = 0; + } + db = dirstat(s); + if(db == nil){ + error: + fprint(2, "ls: %s: %r\n", s); + return 1; + } + if(db->qid.type&QTDIR && dflag==0){ + output(); + fd = open(s, OREAD); + if(fd == -1) + goto error; + n = dirreadall(fd, &db); + if(n < 0) + goto error; + growto(ndir+n); + for(i=0; i<n; i++){ + dirbuf[ndir+i].d = db+i; + dirbuf[ndir+i].prefix = multi? s : 0; + } + ndir += n; + close(fd); + output(); + }else{ + growto(ndir+1); + dirbuf[ndir].d = db; + dirbuf[ndir].prefix = 0; + p = utfrrune(s, '/'); + if(p){ + dirbuf[ndir].prefix = s; + *p = 0; + /* restore original name; don't use result of stat */ + dirbuf[ndir].d->name = strdup(p+1); + } + ndir++; + } + return 0; +} + +void +output(void) +{ + int i; + char buf[4096]; + char *s; + + if(!nflag) + qsort(dirbuf, ndir, sizeof dirbuf[0], (int (*)(const void*, const void*))compar); + for(i=0; i<ndir; i++) + dowidths(dirbuf[i].d); + for(i=0; i<ndir; i++) { + if(!pflag && (s = dirbuf[i].prefix)) { + if(strcmp(s, "/") ==0) /* / is a special case */ + s = ""; + sprint(buf, "%s/%s", s, dirbuf[i].d->name); + format(dirbuf[i].d, buf); + } else + format(dirbuf[i].d, dirbuf[i].d->name); + } + ndir = 0; + Bflush(&bin); +} + +void +dowidths(Dir *db) +{ + char buf[256]; + int n; + + if(sflag) { + n = sprint(buf, "%llud", (db->length+1023)/1024); + if(n > swidth) + swidth = n; + } + if(qflag) { + n = sprint(buf, "%lud", db->qid.vers); + if(n > qwidth) + qwidth = n; + } + if(mflag) { + n = snprint(buf, sizeof buf, "[%s]", db->muid); + if(n > mwidth) + mwidth = n; + } + if(lflag) { + n = sprint(buf, "%ud", db->dev); + if(n > vwidth) + vwidth = n; + n = strlen(db->uid); + if(n > uwidth) + uwidth = n; + n = sprint(buf, "%llud", db->length); + n += strlen(db->gid); + if(n > glwidth) + glwidth = n; + } +} + +char* +fileflag(Dir *db) +{ + if(Fflag == 0) + return ""; + if(QTDIR & db->qid.type) + return "/"; + if(0111 & db->mode) + return "*"; + return ""; +} + +void +format(Dir *db, char *name) +{ + int i; + + if(sflag) + Bprint(&bin, "%*llud ", + swidth, (db->length+1023)/1024); + if(mflag){ + Bprint(&bin, "[%s] ", db->muid); + for(i=2+strlen(db->muid); i<mwidth; i++) + Bprint(&bin, " "); + } + if(qflag) + Bprint(&bin, "(%.16llux %*lud %.2ux) ", + db->qid.path, + qwidth, db->qid.vers, + db->qid.type); + if(lflag) + Bprint(&bin, + Qflag? "%M %C %*ud %*s %s %*llud %s %s\n" : "%M %C %*ud %*s %s %*llud %s %q\n", + db->mode, db->type, + vwidth, db->dev, + -uwidth, db->uid, + db->gid, + (int)(glwidth-strlen(db->gid)), db->length, + asciitime(uflag? db->atime : db->mtime), name); + else + Bprint(&bin, + Qflag? "%s%s\n" : "%q%s\n", + name, fileflag(db)); +} + +void +growto(long n) +{ + if(n <= ndirbuf) + return; + ndirbuf = n; + dirbuf=(NDir *)realloc(dirbuf, ndirbuf*sizeof(NDir)); + if(dirbuf == 0){ + fprint(2, "ls: malloc fail\n"); + exits("malloc fail"); + } +} + +int +compar(NDir *a, NDir *b) +{ + long i; + Dir *ad, *bd; + + ad = a->d; + bd = b->d; + + if(tflag){ + if(uflag) + i = bd->atime-ad->atime; + else + i = bd->mtime-ad->mtime; + }else{ + if(a->prefix && b->prefix){ + i = strcmp(a->prefix, b->prefix); + if(i == 0) + i = strcmp(ad->name, bd->name); + }else if(a->prefix){ + i = strcmp(a->prefix, bd->name); + if(i == 0) + i = 1; /* a is longer than b */ + }else if(b->prefix){ + i = strcmp(ad->name, b->prefix); + if(i == 0) + i = -1; /* b is longer than a */ + }else + i = strcmp(ad->name, bd->name); + } + if(i == 0) + i = (a<b? -1 : 1); + if(rflag) + i = -i; + return i; +} + +char* +asciitime(long l) +{ + static char buf[32]; + char *t; + + t = ctime(l); + /* 6 months in the past or a day in the future */ + if(l<clk-180L*24*60*60 || clk+24L*60*60<l){ + memmove(buf, t+4, 7); /* month and day */ + memmove(buf+7, t+23, 5); /* year */ + }else + memmove(buf, t+4, 12); /* skip day of week */ + buf[12] = 0; + return buf; +} + diff --git a/src/cmd/md5sum.C b/src/cmd/md5sum.C new file mode 100644 index 00000000..8eabbf62 --- /dev/null +++ b/src/cmd/md5sum.C @@ -0,0 +1,61 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <libsec.h> + +static int +digestfmt(Fmt *fmt) +{ + char buf[MD5dlen*2+1]; + uchar *p; + int i; + + p = va_arg(fmt->args, uchar*); + for(i=0; i<MD5dlen; i++) + sprint(buf+2*i, "%.2ux", p[i]); + return fmtstrcpy(fmt, buf); +} + +static void +sum(int fd, char *name) +{ + int n; + uchar buf[8192], digest[MD5dlen]; + DigestState *s; + + s = md5(nil, 0, nil, nil); + while((n = read(fd, buf, sizeof buf)) > 0) + md5(buf, n, nil, s); + md5(nil, 0, digest, s); + if(name == nil) + print("%M\n", digest); + else + print("%M\t%s\n", digest, name); +} + +void +main(int argc, char *argv[]) +{ + int i, fd; + + ARGBEGIN{ + default: + fprint(2, "usage: md5sum [file...]\n"); + exits("usage"); + }ARGEND + + fmtinstall('M', digestfmt); + + if(argc == 0) + sum(0, nil); + else for(i = 0; i < argc; i++){ + fd = open(argv[i], OREAD); + if(fd < 0){ + fprint(2, "md5sum: can't open %s: %r\n", argv[i]); + continue; + } + sum(fd, argv[i]); + close(fd); + } + exits(nil); +} diff --git a/src/cmd/mkdir.C b/src/cmd/mkdir.C new file mode 100644 index 00000000..5bfa6773 --- /dev/null +++ b/src/cmd/mkdir.C @@ -0,0 +1,26 @@ +#include <u.h> +#include <libc.h> + +void +main(int argc, char *argv[]) +{ + int i, f; + char *e; + + e = nil; + for(i=1; i<argc; i++){ + if(access(argv[i], 0) == AEXIST){ + fprint(2, "mkdir: %s already exists\n", argv[i]); + e = "error"; + continue; + } + f = create(argv[i], OREAD, DMDIR | 0777L); + if(f < 0){ + fprint(2, "mkdir: can't create %s: %r\n", argv[i]); + e = "error"; + continue; + } + close(f); + } + exits(e); +} diff --git a/src/cmd/mkfile b/src/cmd/mkfile new file mode 100644 index 00000000..72a2320a --- /dev/null +++ b/src/cmd/mkfile @@ -0,0 +1,13 @@ +PLAN9=../.. +<$PLAN9/src/mkhdr + +TARG=`ls *.c | sed 's/\.c//'` +LDFLAGS=$LDFLAGS -lsec -lregexp9 -l9 -lbio -lfmt -lutf + +<$PLAN9/src/mkmany + +BUGGERED='CVS|oplumb|plumb|plumb2|mk|vac' +DIRS=`ls -l |sed -n 's/^d.* //p' |egrep -v "$BUGGERED"` + +<$PLAN9/src/mkdirs + diff --git a/src/cmd/rm.c b/src/cmd/rm.c new file mode 100644 index 00000000..6066543e --- /dev/null +++ b/src/cmd/rm.c @@ -0,0 +1,104 @@ +#include <u.h> +#include <libc.h> + +#define rmdir p9rmdir + +char errbuf[ERRMAX]; +int ignerr = 0; + +void +err(char *f) +{ + if(!ignerr){ + errbuf[0] = '\0'; + errstr(errbuf, sizeof errbuf); + fprint(2, "rm: %s: %s\n", f, errbuf); + } +} + +/* + * f is a non-empty directory. Remove its contents and then it. + */ +void +rmdir(char *f) +{ + char *name; + int fd, i, j, n, ndir, nname; + Dir *dirbuf; + + fd = open(f, OREAD); + if(fd < 0){ + err(f); + return; + } + n = dirreadall(fd, &dirbuf); + close(fd); + if(n < 0){ + err("dirreadall"); + return; + } + + nname = strlen(f)+1+STATMAX+1; /* plenty! */ + name = malloc(nname); + if(name == 0){ + err("memory allocation"); + return; + } + + ndir = 0; + for(i=0; i<n; i++){ + snprint(name, nname, "%s/%s", f, dirbuf[i].name); + if(remove(name) != -1) + dirbuf[i].qid.type = QTFILE; /* so we won't recurse */ + else{ + if(dirbuf[i].qid.type & QTDIR) + ndir++; + else + err(name); + } + } + if(ndir) + for(j=0; j<n; j++) + if(dirbuf[j].qid.type & QTDIR){ + snprint(name, nname, "%s/%s", f, dirbuf[j].name); + rmdir(name); + } + if(remove(f) == -1) + err(f); + free(name); + free(dirbuf); +} +void +main(int argc, char *argv[]) +{ + int i; + int recurse; + char *f; + Dir *db; + + ignerr = 0; + recurse = 0; + ARGBEGIN{ + case 'r': + recurse = 1; + break; + case 'f': + ignerr = 1; + break; + default: + fprint(2, "usage: rm [-fr] file ...\n"); + exits("usage"); + }ARGEND + for(i=0; i<argc; i++){ + f = argv[i]; + if(remove(f) != -1) + continue; + db = nil; + if(recurse && (db=dirstat(f))!=nil && (db->qid.type&QTDIR)) + rmdir(f); + else + err(f); + free(db); + } + exits(errbuf); +} diff --git a/src/cmd/seq.c b/src/cmd/seq.c new file mode 100644 index 00000000..062bb8a4 --- /dev/null +++ b/src/cmd/seq.c @@ -0,0 +1,92 @@ +#include <u.h> +#include <libc.h> + +double min = 1.0; +double max = 0.0; +double incr = 1.0; +int constant = 0; +int nsteps; +char *format; + +void +usage(void) +{ + fprint(2, "usage: seq [-fformat] [-w] [first [incr]] last\n"); + exits("usage"); +} + +void +buildfmt(void) +{ + int i; + char *dp; + int w, p, maxw, maxp; + static char fmt[16]; + char buf[32]; + + format = "%g\n"; + if(!constant) + return; + maxw = 0; + maxp = 0; + for(i=0; i<=nsteps; i++){ + sprint(buf, "%g", min+i*incr); + if(strchr(buf, 'e')!=0) + return; + dp = strchr(buf,'.'); + w = dp==0? strlen(buf): dp-buf; + p = dp==0? 0: strlen(strchr(buf,'.')+1); + if(w>maxw) + maxw = w; + if(p>maxp) + maxp = p; + } + if(maxp > 0) + maxw += maxp+1; + sprint(fmt,"%%%d.%df\n", maxw, maxp); + format = fmt; +} + +void +main(int argc, char *argv[]){ + int i, j, n; + char buf[256], ffmt[4096]; + + ARGBEGIN{ + case 'w': + constant++; + break; + case 'f': + format = ARGF(); + if(format[strlen(format)-1] != '\n'){ + sprint(ffmt, "%s\n", format); + format = ffmt; + } + break; + default: + goto out; + }ARGEND + out: + if(argc<1 || argc>3) + usage(); + max = atof(argv[argc-1]); + if(argc > 1) + min = atof(argv[0]); + if(argc > 2) + incr = atof(argv[1]); + if(incr == 0){ + fprint(2, "seq: zero increment\n"); + exits("zero increment"); + } + nsteps = (max-min)/incr+.5; + if(!format) + buildfmt(); + for(i=0; i<=nsteps; i++){ + n = sprint(buf, format, min+i*incr); + if(constant) + for(j=0; buf[j]==' '; j++) + buf[j] ='0'; + write(1, buf, n); + } + exits(0); +} diff --git a/src/cmd/sha1sum.c b/src/cmd/sha1sum.c new file mode 100644 index 00000000..d8c449fa --- /dev/null +++ b/src/cmd/sha1sum.c @@ -0,0 +1,61 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <libsec.h> + +static int +digestfmt(Fmt *fmt) +{ + char buf[SHA1dlen*2+1]; + uchar *p; + int i; + + p = va_arg(fmt->args, uchar*); + for(i=0; i<SHA1dlen; i++) + sprint(buf+2*i, "%.2ux", p[i]); + return fmtstrcpy(fmt, buf); +} + +static void +sum(int fd, char *name) +{ + int n; + uchar buf[8192], digest[SHA1dlen]; + DigestState *s; + + s = sha1(nil, 0, nil, nil); + while((n = read(fd, buf, sizeof buf)) > 0) + sha1(buf, n, nil, s); + sha1(nil, 0, digest, s); + if(name == nil) + print("%M\n", digest); + else + print("%M\t%s\n", digest, name); +} + +void +main(int argc, char *argv[]) +{ + int i, fd; + + ARGBEGIN{ + default: + fprint(2, "usage: sha1sum [file...]\n"); + exits("usage"); + }ARGEND + + fmtinstall('M', digestfmt); + + if(argc == 0) + sum(0, nil); + else for(i = 0; i < argc; i++){ + fd = open(argv[i], OREAD); + if(fd < 0){ + fprint(2, "sha1sum: can't open %s: %r\n", argv[i]); + continue; + } + sum(fd, argv[i]); + close(fd); + } + exits(nil); +} diff --git a/src/cmd/sleep.c b/src/cmd/sleep.c new file mode 100644 index 00000000..68cdc7a4 --- /dev/null +++ b/src/cmd/sleep.c @@ -0,0 +1,13 @@ +#include <u.h> +#include <libc.h> + +void +main(int argc, char *argv[]) +{ + long secs; + + if(argc>1) + for(secs = atol(argv[1]); secs > 0; secs--) + sleep(1000); + exits(0); +} diff --git a/src/cmd/sort.c b/src/cmd/sort.c new file mode 100644 index 00000000..359965e8 --- /dev/null +++ b/src/cmd/sort.c @@ -0,0 +1,1767 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +/* +bugs: + 00/ff for end of file can conflict with 00/ff characters +*/ + +enum +{ + Nline = 100000, /* default max number of lines saved in memory */ + Nmerge = 10, /* max number of temporary files merged */ + Nfield = 20, /* max number of argument fields */ + + Bflag = 1<<0, /* flags per field */ + B1flag = 1<<1, + + Dflag = 1<<2, + Fflag = 1<<3, + Gflag = 1<<4, + Iflag = 1<<5, + Mflag = 1<<6, + Nflag = 1<<7, + Rflag = 1<<8, + Wflag = 1<<9, + + NSstart = 0, /* states for number to key decoding */ + NSsign, + NSzero, + NSdigit, + NSpoint, + NSfract, + NSzerofract, + NSexp, + NSexpsign, + NSexpdigit, +}; + +typedef struct Line Line; +typedef struct Key Key; +typedef struct Merge Merge; +typedef struct Field Field; + +struct Line +{ + Key* key; + int llen; /* always >= 1 */ + uchar line[1]; /* always ends in '\n' */ +}; + +struct Merge +{ + Key* key; /* copy of line->key so (Line*) looks like (Merge*) */ + Line* line; /* line at the head of a merged temp file */ + int fd; /* file descriptor */ + Biobuf b; /* iobuf for reading a temp file */ +}; + +struct Key +{ + int klen; + uchar key[1]; +}; + +struct Field +{ + int beg1; + int beg2; + int end1; + int end2; + + long flags; + uchar mapto[256]; + + void (*dokey)(Key*, uchar*, uchar*, Field*); +}; + +struct args +{ + char* ofile; + char* tname; + Rune tabchar; + char cflag; + char uflag; + char vflag; + int nfield; + int nfile; + Field field[Nfield]; + + Line** linep; + long nline; /* number of lines in this temp file */ + long lineno; /* overall ordinal for -s option */ + int ntemp; + long mline; /* max lines per file */ +} args; + +extern int latinmap[]; +extern Rune* month[12]; + +void buildkey(Line*); +void doargs(int, char*[]); +void dofield(char*, int*, int*, int, int); +void dofile(Biobuf*); +void dokey_(Key*, uchar*, uchar*, Field*); +void dokey_dfi(Key*, uchar*, uchar*, Field*); +void dokey_gn(Key*, uchar*, uchar*, Field*); +void dokey_m(Key*, uchar*, uchar*, Field*); +void dokey_r(Key*, uchar*, uchar*, Field*); +void done(char*); +int kcmp(Key*, Key*); +void makemapd(Field*); +void makemapm(Field*); +void mergefiles(int, int, Biobuf*); +void mergeout(Biobuf*); +void newfield(void); +Line* newline(Biobuf*); +void nomem(void); +void notifyf(void*, char*); +void printargs(void); +void printout(Biobuf*); +void setfield(int, int); +uchar* skip(uchar*, int, int, int, int); +void sort4(void*, ulong); +char* tempfile(int); +void tempout(void); +void lineout(Biobuf*, Line*); + +void +main(int argc, char *argv[]) +{ + int i, f; + char *s; + Biobuf bbuf; + + notify(notifyf); /**/ + doargs(argc, argv); + if(args.vflag) + printargs(); + + for(i=1; i<argc; i++) { + s = argv[i]; + if(s == 0) + continue; + if(strcmp(s, "-") == 0) { + Binit(&bbuf, 0, OREAD); + dofile(&bbuf); + Bterm(&bbuf); + continue; + } + f = open(s, OREAD); + if(f < 0) { + fprint(2, "sort: open %s: %r\n", s); + done("open"); + } + Binit(&bbuf, f, OREAD); + dofile(&bbuf); + Bterm(&bbuf); + close(f); + } + if(args.nfile == 0) { + Binit(&bbuf, 0, OREAD); + dofile(&bbuf); + Bterm(&bbuf); + } + if(args.cflag) + done(0); + if(args.vflag) + fprint(2, "=========\n"); + + f = 1; + if(args.ofile) { + f = create(args.ofile, OWRITE, 0666); + if(f < 0) { + fprint(2, "sort: create %s: %r\n", args.ofile); + done("create"); + } + } + + Binit(&bbuf, f, OWRITE); + if(args.ntemp) { + tempout(); + mergeout(&bbuf); + } else { + printout(&bbuf); + } + Bterm(&bbuf); + done(0); +} + +void +dofile(Biobuf *b) +{ + Line *l, *ol; + int n; + + if(args.cflag) { + ol = newline(b); + if(ol == 0) + return; + for(;;) { + l = newline(b); + if(l == 0) + break; + n = kcmp(ol->key, l->key); + if(n > 0 || (n == 0 && args.uflag)) { + fprint(2, "sort: -c file not in sort\n"); /**/ + done("order"); + } + free(ol->key); + free(ol); + ol = l; + } + return; + } + + if(args.linep == 0) { + args.linep = malloc(args.mline * sizeof(args.linep)); + if(args.linep == 0) + nomem(); + } + for(;;) { + l = newline(b); + if(l == 0) + break; + if(args.nline >= args.mline) + tempout(); + args.linep[args.nline] = l; + args.nline++; + args.lineno++; + } +} + +void +notifyf(void *a, char *s) +{ + USED(a); + if(strcmp(s, "interrupt") == 0) + done(0); + if(strcmp(s, "hangup") == 0) + done(0); + if(strcmp(s, "kill") == 0) + done(0); + if(strncmp(s, "sys: write on closed pipe", 25) == 0) + done(0); + fprint(2, "sort: note: %s\n", s); + abort(); +} + +Line* +newline(Biobuf *b) +{ + Line *l; + char *p; + int n, c; + + p = Brdline(b, '\n'); + n = Blinelen(b); + if(p == 0) { + if(n == 0) + return 0; + l = 0; + for(n=0;;) { + if((n & 31) == 0) { + l = realloc(l, sizeof(Line) + + (n+31)*sizeof(l->line[0])); + if(l == 0) + nomem(); + } + c = Bgetc(b); + if(c < 0) { + fprint(2, "sort: newline added\n"); + c = '\n'; + } + l->line[n++] = c; + if(c == '\n') + break; + } + l->llen = n; + buildkey(l); + return l; + } + l = malloc(sizeof(Line) + + (n-1)*sizeof(l->line[0])); + if(l == 0) + nomem(); + l->llen = n; + memmove(l->line, p, n); + buildkey(l); + return l; +} + +void +lineout(Biobuf *b, Line *l) +{ + int n, m; + + n = l->llen; + m = Bwrite(b, l->line, n); + if(n != m) + exits("write"); +} + +void +tempout(void) +{ + long n; + Line **lp, *l; + char *tf; + int f; + Biobuf tb; + + sort4(args.linep, args.nline); + tf = tempfile(args.ntemp); + args.ntemp++; + f = create(tf, OWRITE, 0666); + if(f < 0) { + fprint(2, "sort: create %s: %r\n", tf); + done("create"); + } + + Binit(&tb, f, OWRITE); + lp = args.linep; + for(n=args.nline; n>0; n--) { + l = *lp++; + lineout(&tb, l); + free(l->key); + free(l); + } + args.nline = 0; + Bterm(&tb); + close(f); +} + +void +done(char *xs) +{ + int i; + + for(i=0; i<args.ntemp; i++) + remove(tempfile(i)); + exits(xs); +} + +void +nomem(void) +{ + fprint(2, "sort: out of memory\n"); + done("mem"); +} + +char* +tempfile(int n) +{ + static char file[100]; + static uint pid; + char *dir; + + dir = "/tmp"; + if(args.tname) + dir = args.tname; + if(strlen(dir) >= nelem(file)-20) { + fprint(2, "temp file directory name is too long: %s\n", dir); + done("tdir"); + } + + if(pid == 0) { + pid = getpid(); + if(pid == 0) { + pid = time(0); + if(pid == 0) + pid = 1; + } + } + + sprint(file, "%s/sort.%.4d.%.4d", dir, pid%10000, n); + return file; +} + +void +mergeout(Biobuf *b) +{ + int n, i, f; + char *tf; + Biobuf tb; + + for(i=0; i<args.ntemp; i+=n) { + n = args.ntemp - i; + if(n > Nmerge) { + tf = tempfile(args.ntemp); + args.ntemp++; + f = create(tf, OWRITE, 0666); + if(f < 0) { + fprint(2, "sort: create %s: %r\n", tf); + done("create"); + } + Binit(&tb, f, OWRITE); + + n = Nmerge; + mergefiles(i, n, &tb); + + Bterm(&tb); + close(f); + } else + mergefiles(i, n, b); + } +} + +void +mergefiles(int t, int n, Biobuf *b) +{ + Merge *m, *mp, **mmp; + Key *ok; + Line *l; + char *tf; + int i, f, nn; + + mmp = malloc(n*sizeof(*mmp)); + mp = malloc(n*sizeof(*mp)); + if(mmp == 0 || mp == 0) + nomem(); + + nn = 0; + m = mp; + for(i=0; i<n; i++,m++) { + tf = tempfile(t+i); + f = open(tf, OREAD); + if(f < 0) { + fprint(2, "sort: reopen %s: %r\n", tf); + done("open"); + } + m->fd = f; + Binit(&m->b, f, OREAD); + mmp[nn] = m; + + l = newline(&m->b); + if(l == 0) + continue; + nn++; + m->line = l; + m->key = l->key; + } + + ok = 0; + for(;;) { + sort4(mmp, nn); + m = *mmp; + if(nn == 0) + break; + for(;;) { + l = m->line; + if(args.uflag && ok && kcmp(ok, l->key) == 0) { + free(l->key); + free(l); + } else { + lineout(b, l); + if(ok) + free(ok); + ok = l->key; + free(l); + } + + l = newline(&m->b); + if(l == 0) { + nn--; + mmp[0] = mmp[nn]; + break; + } + m->line = l; + m->key = l->key; + if(nn > 1 && kcmp(mmp[0]->key, mmp[1]->key) > 0) + break; + } + } + if(ok) + free(ok); + + m = mp; + for(i=0; i<n; i++,m++) { + Bterm(&m->b); + close(m->fd); + } + + free(mp); + free(mmp); +} + +int +kcmp(Key *ka, Key *kb) +{ + int n, m; + + /* + * set n to length of smaller key + */ + n = ka->klen; + m = kb->klen; + if(n > m) + n = m; + return memcmp(ka->key, kb->key, n); +} + +void +printout(Biobuf *b) +{ + long n; + Line **lp, *l; + Key *ok; + + sort4(args.linep, args.nline); + lp = args.linep; + ok = 0; + for(n=args.nline; n>0; n--) { + l = *lp++; + if(args.uflag && ok && kcmp(ok, l->key) == 0) + continue; + lineout(b, l); + ok = l->key; + } +} + +void +setfield(int n, int c) +{ + Field *f; + + f = &args.field[n]; + switch(c) { + default: + fprint(2, "sort: unknown option: field.%C\n", c); + done("option"); + case 'b': /* skip blanks */ + f->flags |= Bflag; + break; + case 'd': /* directory order */ + f->flags |= Dflag; + break; + case 'f': /* fold case */ + f->flags |= Fflag; + break; + case 'g': /* floating point -n case */ + f->flags |= Gflag; + break; + case 'i': /* ignore non-ascii */ + f->flags |= Iflag; + break; + case 'M': /* month */ + f->flags |= Mflag; + break; + case 'n': /* numbers */ + f->flags |= Nflag; + break; + case 'r': /* reverse */ + f->flags |= Rflag; + break; + case 'w': /* ignore white */ + f->flags |= Wflag; + break; + } +} + +void +dofield(char *s, int *n1, int *n2, int off1, int off2) +{ + int c, n; + + c = *s++; + if(c >= '0' && c <= '9') { + n = 0; + while(c >= '0' && c <= '9') { + n = n*10 + (c-'0'); + c = *s++; + } + n -= off1; /* posix committee: rot in hell */ + if(n < 0) { + fprint(2, "sort: field offset must be positive\n"); + done("option"); + } + *n1 = n; + } + if(c == '.') { + c = *s++; + if(c >= '0' && c <= '9') { + n = 0; + while(c >= '0' && c <= '9') { + n = n*10 + (c-'0'); + c = *s++; + } + n -= off2; + if(n < 0) { + fprint(2, "sort: character offset must be positive\n"); + done("option"); + } + *n2 = n; + } + } + while(c != 0) { + setfield(args.nfield, c); + c = *s++; + } +} + +void +printargs(void) +{ + int i, n; + Field *f; + char *prefix; + + fprint(2, "sort"); + for(i=0; i<=args.nfield; i++) { + f = &args.field[i]; + prefix = " -"; + if(i) { + n = f->beg1; + if(n >= 0) + fprint(2, " +%d", n); + else + fprint(2, " +*"); + n = f->beg2; + if(n >= 0) + fprint(2, ".%d", n); + else + fprint(2, ".*"); + + if(f->flags & B1flag) + fprint(2, "b"); + + n = f->end1; + if(n >= 0) + fprint(2, " -%d", n); + else + fprint(2, " -*"); + n = f->end2; + if(n >= 0) + fprint(2, ".%d", n); + else + fprint(2, ".*"); + prefix = ""; + } + if(f->flags & Bflag) + fprint(2, "%sb", prefix); + if(f->flags & Dflag) + fprint(2, "%sd", prefix); + if(f->flags & Fflag) + fprint(2, "%sf", prefix); + if(f->flags & Gflag) + fprint(2, "%sg", prefix); + if(f->flags & Iflag) + fprint(2, "%si", prefix); + if(f->flags & Mflag) + fprint(2, "%sM", prefix); + if(f->flags & Nflag) + fprint(2, "%sn", prefix); + if(f->flags & Rflag) + fprint(2, "%sr", prefix); + if(f->flags & Wflag) + fprint(2, "%sw", prefix); + } + if(args.cflag) + fprint(2, " -c"); + if(args.uflag) + fprint(2, " -u"); + if(args.ofile) + fprint(2, " -o %s", args.ofile); + if(args.mline != Nline) + fprint(2, " -l %ld", args.mline); + fprint(2, "\n"); +} + +void +newfield(void) +{ + int n; + Field *f; + + n = args.nfield + 1; + if(n >= Nfield) { + fprint(2, "sort: too many fields specified\n"); + done("option"); + } + args.nfield = n; + f = &args.field[n]; + f->beg1 = -1; + f->beg2 = -1; + f->end1 = -1; + f->end2 = -1; +} + +void +doargs(int argc, char *argv[]) +{ + int i, c, hadplus; + char *s, *p, *q; + Field *f; + + hadplus = 0; + args.mline = Nline; + for(i=1; i<argc; i++) { + s = argv[i]; + c = *s++; + if(c == '-') { + c = *s; + if(c == 0) /* forced end of arg marker */ + break; + argv[i] = 0; /* clobber args processed */ + if(c == '.' || (c >= '0' && c <= '9')) { + if(!hadplus) + newfield(); + f = &args.field[args.nfield]; + dofield(s, &f->end1, &f->end2, 0, 0); + hadplus = 0; + continue; + } + + while(c = *s++) + switch(c) { + case '-': /* end of options */ + i = argc; + continue; + case 'T': /* temp directory */ + if(*s == 0) { + i++; + if(i < argc) { + args.tname = argv[i]; + argv[i] = 0; + } + } else + args.tname = s; + s = strchr(s, 0); + break; + case 'o': /* output file */ + if(*s == 0) { + i++; + if(i < argc) { + args.ofile = argv[i]; + argv[i] = 0; + } + } else + args.ofile = s; + s = strchr(s, 0); + break; + case 'k': /* posix key (what were they thinking?) */ + p = 0; + if(*s == 0) { + i++; + if(i < argc) { + p = argv[i]; + argv[i] = 0; + } + } else + p = s; + s = strchr(s, 0); + if(p == 0) + break; + + newfield(); + q = strchr(p, ','); + if(q) + *q++ = 0; + f = &args.field[args.nfield]; + dofield(p, &f->beg1, &f->beg2, 1, 1); + if(f->flags & Bflag) { + f->flags |= B1flag; + f->flags &= ~Bflag; + } + if(q) { + dofield(q, &f->end1, &f->end2, 1, 0); + if(f->end2 <= 0) + f->end1++; + } + hadplus = 0; + break; + case 't': /* tab character */ + if(*s == 0) { + i++; + if(i < argc) { + chartorune(&args.tabchar, argv[i]); + argv[i] = 0; + } + } else + s += chartorune(&args.tabchar, s); + if(args.tabchar == '\n') { + fprint(2, "aw come on, rob\n"); + done("rob"); + } + break; + case 'c': /* check order */ + args.cflag = 1; + break; + case 'u': /* unique */ + args.uflag = 1; + break; + case 'v': /* debugging noise */ + args.vflag = 1; + break; + case 'l': + if(*s == 0) { + i++; + if(i < argc) { + args.mline = atol(argv[i]); + argv[i] = 0; + } + } else + args.mline = atol(s); + s = strchr(s, 0); + break; + + case 'M': /* month */ + case 'b': /* skip blanks */ + case 'd': /* directory order */ + case 'f': /* fold case */ + case 'g': /* floating numbers */ + case 'i': /* ignore non-ascii */ + case 'n': /* numbers */ + case 'r': /* reverse */ + case 'w': /* ignore white */ + if(args.nfield > 0) + fprint(2, "sort: global field set after -k\n"); + setfield(0, c); + break; + case 'm': + /* option m silently ignored but required by posix */ + break; + default: + fprint(2, "sort: unknown option: -%C\n", c); + done("option"); + } + continue; + } + if(c == '+') { + argv[i] = 0; /* clobber args processed */ + c = *s; + if(c == '.' || (c >= '0' && c <= '9')) { + newfield(); + f = &args.field[args.nfield]; + dofield(s, &f->beg1, &f->beg2, 0, 0); + if(f->flags & Bflag) { + f->flags |= B1flag; + f->flags &= ~Bflag; + } + hadplus = 1; + continue; + } + fprint(2, "sort: unknown option: +%C\n", c); + done("option"); + } + args.nfile++; + } + + for(i=0; i<=args.nfield; i++) { + f = &args.field[i]; + + /* + * global options apply to fields that + * specify no options + */ + if(f->flags == 0) { + f->flags = args.field[0].flags; + if(args.field[0].flags & Bflag) + f->flags |= B1flag; + } + + + /* + * build buildkey specification + */ + switch(f->flags & ~(Bflag|B1flag)) { + default: + fprint(2, "sort: illegal combination of flags: %lx\n", f->flags); + done("option"); + case 0: + f->dokey = dokey_; + break; + case Rflag: + f->dokey = dokey_r; + break; + case Gflag: + case Nflag: + case Gflag|Nflag: + case Gflag|Rflag: + case Nflag|Rflag: + case Gflag|Nflag|Rflag: + f->dokey = dokey_gn; + break; + case Mflag: + case Mflag|Rflag: + f->dokey = dokey_m; + makemapm(f); + break; + case Dflag: + case Dflag|Fflag: + case Dflag|Fflag|Iflag: + case Dflag|Fflag|Iflag|Rflag: + case Dflag|Fflag|Iflag|Rflag|Wflag: + case Dflag|Fflag|Iflag|Wflag: + case Dflag|Fflag|Rflag: + case Dflag|Fflag|Rflag|Wflag: + case Dflag|Fflag|Wflag: + case Dflag|Iflag: + case Dflag|Iflag|Rflag: + case Dflag|Iflag|Rflag|Wflag: + case Dflag|Iflag|Wflag: + case Dflag|Rflag: + case Dflag|Rflag|Wflag: + case Dflag|Wflag: + case Fflag: + case Fflag|Iflag: + case Fflag|Iflag|Rflag: + case Fflag|Iflag|Rflag|Wflag: + case Fflag|Iflag|Wflag: + case Fflag|Rflag: + case Fflag|Rflag|Wflag: + case Fflag|Wflag: + case Iflag: + case Iflag|Rflag: + case Iflag|Rflag|Wflag: + case Iflag|Wflag: + case Wflag: + f->dokey = dokey_dfi; + makemapd(f); + break; + } + } + + /* + * random spot checks + */ + if(args.nfile > 1 && args.cflag) { + fprint(2, "sort: -c can have at most one input file\n"); + done("option"); + } + return; +} + +uchar* +skip(uchar *l, int n1, int n2, int bflag, int endfield) +{ + int i, c, tc; + Rune r; + + if(endfield && n1 < 0) + return 0; + + c = *l++; + tc = args.tabchar; + if(tc) { + if(tc < Runeself) { + for(i=n1; i>0; i--) { + while(c != tc) { + if(c == '\n') + return 0; + c = *l++; + } + if(!(endfield && i == 1)) + c = *l++; + } + } else { + l--; + l += chartorune(&r, (char*)l); + for(i=n1; i>0; i--) { + while(r != tc) { + if(r == '\n') + return 0; + l += chartorune(&r, (char*)l); + } + if(!(endfield && i == 1)) + l += chartorune(&r, (char*)l); + } + c = r; + } + } else { + for(i=n1; i>0; i--) { + while(c == ' ' || c == '\t') + c = *l++; + while(c != ' ' && c != '\t') { + if(c == '\n') + return 0; + c = *l++; + } + } + } + + if(bflag) + while(c == ' ' || c == '\t') + c = *l++; + + l--; + for(i=n2; i>0; i--) { + c = *l; + if(c < Runeself) { + if(c == '\n') + return 0; + l++; + continue; + } + l += chartorune(&r, (char*)l); + } + return l; +} + +void +dokey_gn(Key *k, uchar *lp, uchar *lpe, Field *f) +{ + uchar *kp; + int c, cl, dp; + int state, nzero, exp, expsign, rflag; + + cl = k->klen + 3; + kp = k->key + cl; /* skip place for sign, exponent[2] */ + + nzero = 0; /* number of trailing zeros */ + exp = 0; /* value of the exponent */ + expsign = 0; /* sign of the exponent */ + dp = 0x4040; /* location of decimal point */ + rflag = f->flags&Rflag; /* xor of rflag and - sign */ + state = NSstart; + + for(;; lp++) { + if(lp >= lpe) + break; + c = *lp; + + if(c == ' ' || c == '\t') { + switch(state) { + case NSstart: + case NSsign: + continue; + } + break; + } + if(c == '+' || c == '-') { + switch(state) { + case NSstart: + state = NSsign; + if(c == '-') + rflag = !rflag; + continue; + case NSexp: + state = NSexpsign; + if(c == '-') + expsign = 1; + continue; + } + break; + } + if(c == '0') { + switch(state) { + case NSdigit: + if(rflag) + c = ~c; + *kp++ = c; + cl++; + nzero++; + dp++; + state = NSdigit; + continue; + case NSfract: + if(rflag) + c = ~c; + *kp++ = c; + cl++; + nzero++; + state = NSfract; + continue; + case NSstart: + case NSsign: + case NSzero: + state = NSzero; + continue; + case NSzerofract: + case NSpoint: + dp--; + state = NSzerofract; + continue; + case NSexpsign: + case NSexp: + case NSexpdigit: + exp = exp*10 + (c - '0'); + state = NSexpdigit; + continue; + } + break; + } + if(c >= '1' && c <= '9') { + switch(state) { + case NSzero: + case NSstart: + case NSsign: + case NSdigit: + if(rflag) + c = ~c; + *kp++ = c; + cl++; + nzero = 0; + dp++; + state = NSdigit; + continue; + case NSzerofract: + case NSpoint: + case NSfract: + if(rflag) + c = ~c; + *kp++ = c; + cl++; + nzero = 0; + state = NSfract; + continue; + case NSexpsign: + case NSexp: + case NSexpdigit: + exp = exp*10 + (c - '0'); + state = NSexpdigit; + continue; + } + break; + } + if(c == '.') { + switch(state) { + case NSstart: + case NSsign: + state = NSpoint; + continue; + case NSzero: + state = NSzerofract; + continue; + case NSdigit: + state = NSfract; + continue; + } + break; + } + if((f->flags & Gflag) && (c == 'e' || c == 'E')) { + switch(state) { + case NSdigit: + case NSfract: + state = NSexp; + continue; + } + break; + } + break; + } + + switch(state) { + /* + * result is zero + */ + case NSstart: + case NSsign: + case NSzero: + case NSzerofract: + case NSpoint: + kp = k->key + k->klen; + k->klen += 2; + kp[0] = 0x20; /* between + and - */ + kp[1] = 0; + return; + /* + * result has exponent + */ + case NSexpsign: + case NSexp: + case NSexpdigit: + if(expsign) + exp = -exp; + dp += exp; + + /* + * result is fixed point number + */ + case NSdigit: + case NSfract: + kp -= nzero; + cl -= nzero; + break; + } + + /* + * end of number + */ + c = 0; + if(rflag) + c = ~c; + *kp = c; + + /* + * sign and exponent + */ + c = 0x30; + if(rflag) { + c = 0x10; + dp = ~dp; + } + kp = k->key + k->klen; + kp[0] = c; + kp[1] = (dp >> 8); + kp[2] = dp; + k->klen = cl+1; +} + +void +dokey_m(Key *k, uchar *lp, uchar *lpe, Field *f) +{ + uchar *kp; + Rune r, place[3]; + int c, cl, pc; + int rflag; + + rflag = f->flags&Rflag; + pc = 0; + + cl = k->klen; + kp = k->key + cl; + + for(;;) { + /* + * get the character + */ + if(lp >= lpe) + break; + c = *lp; + if(c >= Runeself) { + lp += chartorune(&r, (char*)lp); + c = r; + } else + lp++; + + if(c < nelem(f->mapto)) { + c = f->mapto[c]; + if(c == 0) + continue; + } + place[pc++] = c; + if(pc < 3) + continue; + for(c=11; c>=0; c--) + if(memcmp(month[c], place, sizeof(place)) == 0) + break; + c += 10; + if(rflag) + c = ~c; + *kp++ = c; + cl++; + break; + } + + c = 0; + if(rflag) + c = ~c; + *kp = c; + k->klen = cl+1; +} + +void +dokey_dfi(Key *k, uchar *lp, uchar *lpe, Field *f) +{ + uchar *kp; + Rune r; + int c, cl, n, rflag; + + cl = k->klen; + kp = k->key + cl; + rflag = f->flags & Rflag; + + for(;;) { + /* + * get the character + */ + if(lp >= lpe) + break; + c = *lp; + if(c >= Runeself) { + lp += chartorune(&r, (char*)lp); + c = r; + } else + lp++; + + /* + * do the various mappings. + * the common case is handled + * completely by the table. + */ + if(c != 0 && c < Runeself) { + c = f->mapto[c]; + if(c) { + *kp++ = c; + cl++; + } + continue; + } + + /* + * for characters out of range, + * the table does not do Rflag. + * ignore is based on mapto[255] + */ + if(c != 0 && c < nelem(f->mapto)) { + c = f->mapto[c]; + if(c == 0) + continue; + } else + if(f->mapto[nelem(f->mapto)-1] == 0) + continue; + + /* + * put it in the key + */ + r = c; + n = runetochar((char*)kp, &r); + kp += n; + cl += n; + if(rflag) + while(n > 0) { + kp[-n] = ~kp[-n]; + n--; + } + } + + /* + * end of key + */ + k->klen = cl+1; + if(rflag) { + *kp = ~0; + return; + } + *kp = 0; +} + +void +dokey_r(Key *k, uchar *lp, uchar *lpe, Field *f) +{ + int cl, n; + uchar *kp; + + USED(f); + n = lpe - lp; + if(n < 0) + n = 0; + cl = k->klen; + kp = k->key + cl; + k->klen = cl+n+1; + + lpe -= 3; + while(lp < lpe) { + kp[0] = ~lp[0]; + kp[1] = ~lp[1]; + kp[2] = ~lp[2]; + kp[3] = ~lp[3]; + kp += 4; + lp += 4; + } + + lpe += 3; + while(lp < lpe) + *kp++ = ~*lp++; + *kp = ~0; +} + +void +dokey_(Key *k, uchar *lp, uchar *lpe, Field *f) +{ + int n, cl; + uchar *kp; + + USED(f); + n = lpe - lp; + if(n < 0) + n = 0; + cl = k->klen; + kp = k->key + cl; + k->klen = cl+n+1; + memmove(kp, lp, n); + kp[n] = 0; +} + +void +buildkey(Line *l) +{ + Key *k; + uchar *lp, *lpe; + int ll, kl, cl, i, n; + Field *f; + + ll = l->llen - 1; + kl = 0; /* allocated length */ + cl = 0; /* current length */ + k = 0; + + for(i=1; i<=args.nfield; i++) { + f = &args.field[i]; + lp = skip(l->line, f->beg1, f->beg2, f->flags&B1flag, 0); + if(lp == 0) + lp = l->line + ll; + lpe = skip(l->line, f->end1, f->end2, f->flags&Bflag, 1); + if(lpe == 0) + lpe = l->line + ll; + n = (lpe - lp) + 1; + if(n <= 0) + n = 1; + if(cl+(n+4) > kl) { + kl = cl+(n+4); + k = realloc(k, sizeof(Key) + + (kl-1)*sizeof(k->key[0])); + if(k == 0) + nomem(); + } + k->klen = cl; + (*f->dokey)(k, lp, lpe, f); + cl = k->klen; + } + + /* + * global comparisons + */ + if(!(args.uflag && cl > 0)) { + f = &args.field[0]; + if(cl+(ll+4) > kl) { + kl = cl+(ll+4); + k = realloc(k, sizeof(Key) + + (kl-1)*sizeof(k->key[0])); + if(k == 0) + nomem(); + } + k->klen = cl; + (*f->dokey)(k, l->line, l->line+ll, f); + cl = k->klen; + } + + l->key = k; + k->klen = cl; + + if(args.vflag) { + write(2, l->line, l->llen); + for(i=0; i<k->klen; i++) { + fprint(2, " %.2x", k->key[i]); + if(k->key[i] == 0x00 || k->key[i] == 0xff) + fprint(2, "\n"); + } + } +} + +void +makemapm(Field *f) +{ + int i, c; + + for(i=0; i<nelem(f->mapto); i++) { + c = 1; + if(i == ' ' || i == '\t') + c = 0; + if(i >= 'a' && i <= 'z') + c = i + ('A' - 'a'); + if(i >= 'A' && i <= 'Z') + c = i; + f->mapto[i] = c; + if(args.vflag) { + if((i & 15) == 0) + fprint(2, " "); + fprint(2, " %.2x", c); + if((i & 15) == 15) + fprint(2, "\n"); + } + } +} + +void +makemapd(Field *f) +{ + int i, j, c; + + for(i=0; i<nelem(f->mapto); i++) { + c = i; + if(f->flags & Iflag) + if(c < 040 || c > 0176) + c = -1; + if((f->flags & Wflag) && c >= 0) + if(c == ' ' || c == '\t') + c = -1; + if((f->flags & Dflag) && c >= 0) + if(!(c == ' ' || c == '\t' || + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9'))) { + for(j=0; latinmap[j]; j+=3) + if(c == latinmap[j+0] || + c == latinmap[j+1]) + break; + if(latinmap[j] == 0) + c = -1; + } + if((f->flags & Fflag) && c >= 0) { + if(c >= 'a' && c <= 'z') + c += 'A' - 'a'; + for(j=0; latinmap[j]; j+=3) + if(c == latinmap[j+0] || + c == latinmap[j+1]) { + c = latinmap[j+2]; + break; + } + } + if((f->flags & Rflag) && c >= 0 && i > 0 && i < Runeself) + c = ~c & 0xff; + if(c < 0) + c = 0; + f->mapto[i] = c; + if(args.vflag) { + if((i & 15) == 0) + fprint(2, " "); + fprint(2, " %.2x", c); + if((i & 15) == 15) + fprint(2, "\n"); + } + } +} + +int latinmap[] = +{ +/* lcase ucase fold */ + 0xe0, 0xc0, 0x41, /* L'à', L'À', L'A', */ + 0xe1, 0xc1, 0x41, /* L'á', L'Á', L'A', */ + 0xe2, 0xc2, 0x41, /* L'â', L'Â', L'A', */ + 0xe4, 0xc4, 0x41, /* L'ä', L'Ä', L'A', */ + 0xe3, 0xc3, 0x41, /* L'ã', L'Ã', L'A', */ + 0xe5, 0xc5, 0x41, /* L'å', L'Å', L'A', */ + 0xe8, 0xc8, 0x45, /* L'è', L'È', L'E', */ + 0xe9, 0xc9, 0x45, /* L'é', L'É', L'E', */ + 0xea, 0xca, 0x45, /* L'ê', L'Ê', L'E', */ + 0xeb, 0xcb, 0x45, /* L'ë', L'Ë', L'E', */ + 0xec, 0xcc, 0x49, /* L'ì', L'Ì', L'I', */ + 0xed, 0xcd, 0x49, /* L'í', L'Í', L'I', */ + 0xee, 0xce, 0x49, /* L'î', L'Î', L'I', */ + 0xef, 0xcf, 0x49, /* L'ï', L'Ï', L'I', */ + 0xf2, 0xd2, 0x4f, /* L'ò', L'Ò', L'O', */ + 0xf3, 0xd3, 0x4f, /* L'ó', L'Ó', L'O', */ + 0xf4, 0xd4, 0x4f, /* L'ô', L'Ô', L'O', */ + 0xf6, 0xd6, 0x4f, /* L'ö', L'Ö', L'O', */ + 0xf5, 0xd5, 0x4f, /* L'õ', L'Õ', L'O', */ + 0xf8, 0xd8, 0x4f, /* L'ø', L'Ø', L'O', */ + 0xf9, 0xd9, 0x55, /* L'ù', L'Ù', L'U', */ + 0xfa, 0xda, 0x55, /* L'ú', L'Ú', L'U', */ + 0xfb, 0xdb, 0x55, /* L'û', L'Û', L'U', */ + 0xfc, 0xdc, 0x55, /* L'ü', L'Ü', L'U', */ + 0xe6, 0xc6, 0x41, /* L'æ', L'Æ', L'A', */ + 0xf0, 0xd0, 0x44, /* L'ð', L'Ð', L'D', */ + 0xf1, 0xd1, 0x4e, /* L'ñ', L'Ñ', L'N', */ + 0xfd, 0xdd, 0x59, /* L'ý', L'Ý', L'Y', */ + 0xe7, 0xc7, 0x43, /* L'ç', L'Ç', L'C', */ + 0, +}; + +Rune LJAN[] = { 'J', 'A', 'N', 0 }; +Rune LFEB[] = { 'F', 'E', 'B', 0 }; +Rune LMAR[] = { 'M', 'A', 'R', 0 }; +Rune LAPR[] = { 'A', 'P', 'R', 0 }; +Rune LMAY[] = { 'M', 'A', 'Y', 0 }; +Rune LJUN[] = { 'J', 'U', 'N', 0 }; +Rune LJUL[] = { 'J', 'U', 'L', 0 }; +Rune LAUG[] = { 'A', 'U', 'G', 0 }; +Rune LSEP[] = { 'S', 'E', 'P', 0 }; +Rune LOCT[] = { 'O', 'C', 'T', 0 }; +Rune LNOV[] = { 'N', 'O', 'V', 0 }; +Rune LDEC[] = { 'D', 'E', 'C', 0 }; + +Rune* month[12] = +{ + LJAN, + LFEB, + LMAR, + LAPR, + LMAY, + LJUN, + LJUL, + LAUG, + LSEP, + LOCT, + LNOV, + LDEC, +}; + +/************** radix sort ***********/ + +enum +{ + Threshold = 14, +}; + +void rsort4(Key***, ulong, int); +void bsort4(Key***, ulong, int); + +void +sort4(void *a, ulong n) +{ + if(n > Threshold) + rsort4((Key***)a, n, 0); + else + bsort4((Key***)a, n, 0); +} + +void +rsort4(Key ***a, ulong n, int b) +{ + Key ***ea, ***t, ***u, **t1, **u1, *k; + Key ***part[257]; + static long count[257]; + long clist[257+257], *cp, *cp1; + int c, lowc, higc; + + /* + * pass 1 over all keys, + * count the number of each key[b]. + * find low count and high count. + */ + lowc = 256; + higc = 0; + ea = a+n; + for(t=a; t<ea; t++) { + k = **t; + n = k->klen; + if(b >= n) { + count[256]++; + continue; + } + c = k->key[b]; + n = count[c]++; + if(n == 0) { + if(c < lowc) + lowc = c; + if(c > higc) + higc = c; + } + } + + /* + * pass 2 over all counts, + * put partition pointers in part[c]. + * save compacted indexes and counts + * in clist[]. + */ + t = a; + n = count[256]; + clist[0] = n; + part[256] = t; + t += n; + + cp1 = clist+1; + cp = count+lowc; + for(c=lowc; c<=higc; c++,cp++) { + n = *cp; + if(n) { + cp1[0] = n; + cp1[1] = c; + cp1 += 2; + part[c] = t; + t += n; + } + } + *cp1 = 0; + + /* + * pass 3 over all counts. + * chase lowest pointer in each partition + * around a permutation until it comes + * back and is stored where it started. + * static array, count[], should be + * reduced to zero entries except maybe + * count[256]. + */ + for(cp1=clist+1; cp1[0]; cp1+=2) { + c = cp1[1]; + cp = count+c; + while(*cp) { + t1 = *part[c]; + for(;;) { + k = *t1; + n = 256; + if(b < k->klen) + n = k->key[b]; + u = part[n]++; + count[n]--; + u1 = *u; + *u = t1; + if(n == c) + break; + t1 = u1; + } + } + } + + /* + * pass 4 over all partitions. + * call recursively. + */ + b++; + t = a + clist[0]; + count[256] = 0; + for(cp1=clist+1; n=cp1[0]; cp1+=2) { + if(n > Threshold) + rsort4(t, n, b); + else + if(n > 1) + bsort4(t, n, b); + t += n; + } +} + +/* + * bubble sort to pick up + * the pieces. + */ +void +bsort4(Key ***a, ulong n, int b) +{ + Key ***i, ***j, ***k, ***l, **t; + Key *ka, *kb; + int n1, n2; + + l = a+n; + j = a; + +loop: + i = j; + j++; + if(j >= l) + return; + + ka = **i; + kb = **j; + n1 = ka->klen - b; + n2 = kb->klen - b; + if(n1 > n2) + n1 = n2; + if(n1 <= 0) + goto loop; + n2 = ka->key[b] - kb->key[b]; + if(n2 == 0) + n2 = memcmp(ka->key+b, kb->key+b, n1); + if(n2 <= 0) + goto loop; + + for(;;) { + k = i+1; + + t = *k; + *k = *i; + *i = t; + + if(i <= a) + goto loop; + + i--; + ka = **i; + kb = *t; + n1 = ka->klen - b; + n2 = kb->klen - b; + if(n1 > n2) + n1 = n2; + if(n1 <= 0) + goto loop; + n2 = ka->key[b] - kb->key[b]; + if(n2 == 0) + n2 = memcmp(ka->key+b, kb->key+b, n1); + if(n2 <= 0) + goto loop; + } +} diff --git a/src/cmd/split.c b/src/cmd/split.c new file mode 100644 index 00000000..c4025e20 --- /dev/null +++ b/src/cmd/split.c @@ -0,0 +1,189 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <ctype.h> +#include <regexp.h> + +char digit[] = "0123456789"; +char *suffix = ""; +char *stem = "x"; +char suff[] = "aa"; +char name[200]; +Biobuf bout; +Biobuf *output = &bout; + +extern int nextfile(void); +extern int matchfile(Resub*); +extern void openf(void); +extern char *fold(char*,int); +extern void usage(void); +extern void badexp(void); + +void +main(int argc, char *argv[]) +{ + Reprog *exp; + char *pattern = 0; + int n = 1000; + char *line; + int xflag = 0; + int iflag = 0; + Biobuf bin; + Biobuf *b = &bin; + char buf[256]; + + ARGBEGIN { + case 'l': + case 'n': + n=atoi(EARGF(usage())); + break; + case 'e': + pattern = strdup(EARGF(usage())); + break; + case 'f': + stem = strdup(EARGF(usage())); + break; + case 's': + suffix = strdup(EARGF(usage())); + break; + case 'x': + xflag++; + break; + case 'i': + iflag++; + break; + default: + usage(); + break; + + } ARGEND; + + if(argc < 0 || argc > 1) + usage(); + + if(argc != 0) { + b = Bopen(argv[0], OREAD); + if(b == nil) { + fprint(2, "split: can't open %s: %r\n", argv[0]); + exits("open"); + } + } else + Binit(b, 0, OREAD); + + if(pattern) { + if(!(exp = regcomp(iflag? fold(pattern,strlen(pattern)): pattern))) + badexp(); + while((line=Brdline(b,'\n')) != 0) { + Resub match[2]; + memset(match, 0, sizeof match); + line[Blinelen(b)-1] = 0; + if(regexec(exp,iflag?fold(line,Blinelen(b)-1):line,match,2)) { + if(matchfile(match) && xflag) + continue; + } else if(output == 0) + nextfile(); /* at most once */ + Bwrite(output, line, Blinelen(b)-1); + Bputc(output, '\n'); + } + } else { + int linecnt = n; + + while((line=Brdline(b,'\n')) != 0) { + if(++linecnt > n) { + nextfile(); + linecnt = 1; + } + Bwrite(output, line, Blinelen(b)); + } + + /* + * in case we didn't end with a newline, tack whatever's + * left onto the last file + */ + while((n = Bread(b, buf, sizeof(buf))) > 0) + Bwrite(output, buf, n); + } + if(b != nil) + Bterm(b); + exits(0); +} + +int +nextfile(void) +{ + static int canopen = 1; + if(suff[0] > 'z') { + if(canopen) + fprint(2, "split: file %szz not split\n",stem); + canopen = 0; + } else { + strcpy(name, stem); + strcat(name, suff); + if(++suff[1] > 'z') + suff[1] = 'a', ++suff[0]; + openf(); + } + return canopen; +} + +int +matchfile(Resub *match) +{ + if(match[1].s.sp) { + int len = match[1].e.ep - match[1].s.sp; + strncpy(name, match[1].s.sp, len); + strcpy(name+len, suffix); + openf(); + return 1; + } + return nextfile(); +} + +void +openf(void) +{ + static int fd = 0; + Bflush(output); + Bterm(output); + if(fd > 0) + close(fd); + fd = create(name,OWRITE,0666); + if(fd < 0) { + fprint(2, "grep: can't create %s: %r\n", name); + exits("create"); + } + Binit(output, fd, OWRITE); +} + +char * +fold(char *s, int n) +{ + static char *fline; + static int linesize = 0; + char *t; + + if(linesize < n+1){ + fline = realloc(fline,n+1); + linesize = n+1; + } + for(t=fline; *t++ = tolower(*s++); ) + continue; + /* we assume the 'A'-'Z' only appear as themselves + * in a utf encoding. + */ + return fline; +} + +void +usage(void) +{ + fprint(2, "usage: split [-n num] [-e exp] [-f stem] [-s suff] [-x] [-i] [file]\n"); + exits("usage"); +} + +void +badexp(void) +{ + fprint(2, "split: bad regular expression\n"); + exits("bad regular expression"); +} diff --git a/src/cmd/strings.c b/src/cmd/strings.c new file mode 100644 index 00000000..dbf57ff8 --- /dev/null +++ b/src/cmd/strings.c @@ -0,0 +1,88 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +Biobuf *fin; +Biobuf fout; + +#define MINSPAN 6 /* Min characters in string */ + +#define BUFSIZE 70 + +void stringit(char *); +int isprint(Rune); + +void +main(int argc, char **argv) +{ + int i; + + Binit(&fout, 1, OWRITE); + if(argc < 2) { + stringit("/fd/0"); + exits(0); + } + + for(i = 1; i < argc; i++) { + if(argc > 2) + print("%s:\n", argv[i]); + + stringit(argv[i]); + } + + exits(0); +} + +void +stringit(char *str) +{ + long posn, start; + int cnt = 0; + long c; + + Rune buf[BUFSIZE]; + + if ((fin = Bopen(str, OREAD)) == 0) { + perror("open"); + return; + } + + start = 0; + posn = Boffset(fin); + while((c = Bgetrune(fin)) >= 0) { + if(isprint(c)) { + if(start == 0) + start = posn; + buf[cnt++] = c; + if(cnt == BUFSIZE-1) { + buf[cnt] = 0; + Bprint(&fout, "%8ld: %S ...\n", start, buf); + start = 0; + cnt = 0; + } + } else { + if(cnt >= MINSPAN) { + buf[cnt] = 0; + Bprint(&fout, "%8ld: %S\n", start, buf); + } + start = 0; + cnt = 0; + } + posn = Boffset(fin); + } + + if(cnt >= MINSPAN){ + buf[cnt] = 0; + Bprint(&fout, "%8ld: %S\n", start, buf); + } + Bterm(fin); +} + +int +isprint(Rune r) +{ + if ((r >= ' ' && r <0x7f) || r > 0xA0) + return 1; + else + return 0; +} diff --git a/src/cmd/sum.c b/src/cmd/sum.c new file mode 100644 index 00000000..de006dbb --- /dev/null +++ b/src/cmd/sum.c @@ -0,0 +1,215 @@ +#include <u.h> +#include <libc.h> + +typedef ulong Sumfn(ulong, void*, uvlong); +extern Sumfn sumr, sum5, sum32; +char *sumfile(char*, Sumfn*); + +void +usage(void) +{ + fprint(2, "Usage: %s [-r5] [files]\n", argv0); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + Sumfn *fn = sum32; + char *exitstr=0, *s; + + ARGBEGIN{ + case 'r': + fn = sumr; + break; + case '5': + fn = sum5; + break; + default: + usage(); + break; + }ARGEND + if(*argv){ + while(*argv) + if(s = sumfile(*argv++, fn)) /* assign = */ + exitstr = s; + }else + exitstr = sumfile(0, fn); + exits(exitstr); +} + +char* +sumfile(char *file, Sumfn *fn) +{ + int fd; + int n; + ulong sum; + uvlong fsize; + char buf[8*1024]; + + if(file){ + if((fd = open(file, OREAD)) < 0){ + errstr(buf, sizeof buf); + fprint(2, "%s: %s: %s\n", argv0, file, buf); + return "can't open"; + } + }else + fd = 0; + fsize = 0; + sum = 0; + while((n=read(fd, buf, sizeof buf)) > 0){ + fsize += n; + sum = (*fn)(sum, buf, n); + } + if(n < 0){ + errstr(buf, sizeof buf); + fprint(2, "%s: %s: read error: %s\n", argv0, file? file:"<stdin>", buf); + if(file) + close(fd); + return "read error"; + } + if(file) + close(fd); + (*fn)(sum, (char*)0, fsize); + if(file) + print(" %s", file); + print("\n"); + return 0; +} + +#define VBSIZE 512 /* system v */ + +ulong +sum5(ulong sum, void *buf, uvlong uvn) +{ + uchar *s, *send; + int n; + + if(buf == 0){ + sum = ((sum>>16)+sum) & 0xFFFF; + print("%.5lud%6lld", sum, (uvn+(VBSIZE-1))/VBSIZE); + return 0; + } + n = uvn; + for(s=buf, send=s+n; s<send; s++) + sum += 0xffff & *s; + return sum; +} + +#define RBSIZE 1024 /* research */ + +ulong +sumr(ulong sum, void *buf, uvlong uvn) +{ + uchar *s, *send; + int n; + + if(buf == 0){ + sum &= 0xFFFF; + print("%.5lud%6lld", sum, (uvn+(RBSIZE-1))/RBSIZE); + return 0; + } + n = uvn; + for(s=buf, send=s+n; s<send; s++) + if(sum & 1) + sum = 0xffff & ((sum>>1)+*s+0x8000); + else + sum = 0xffff & ((sum>>1)+*s); + return sum; +} + +extern ulong crc_table[256]; + +ulong +sum32(ulong lcrc, void *buf, uvlong uvn) +{ + uchar *s = buf; + ulong crc = lcrc; + int n; + + n = uvn; + if(buf == 0){ + char x[4]; + + x[0] = (n>>24)^0xCC; /* encode the length but make n==0 not 0 */ + x[1] = (n>>16)^0x55; + x[2] = (n>>8)^0xCC; + x[3] = (n)^0x55; + crc = sum32(lcrc, x, 4); + print("%.8lux %6lld", crc, uvn); + return 0; + } + while(n-- > 0) + crc = crc_table[(crc^*s++)&0xff] ^ (crc>>8); + return crc; +} + +/* + * CRC 035556101440 + */ +ulong crc_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +}; diff --git a/src/cmd/tail.c b/src/cmd/tail.c new file mode 100644 index 00000000..448b3eaf --- /dev/null +++ b/src/cmd/tail.c @@ -0,0 +1,362 @@ +#include <u.h> +#include <libc.h> +#include <ctype.h> +#include <bio.h> + +/* + * tail command, posix plus v10 option -r. + * the simple command tail -c, legal in v10, is illegal + */ + +long count; +int anycount; +int follow; +int file = 0; +char* umsg = "usage: tail [-n N] [-c N] [-f] [-r] [+-N[bc][fr]] [file]"; + +Biobuf bout; +enum +{ + BEG, + END +} origin = END; +enum +{ + CHARS, + LINES +} units = LINES; +enum +{ + FWD, + REV +} dir = FWD; + +extern void copy(void); +extern void fatal(char*); +extern int getnumber(char*); +extern void keep(void); +extern void reverse(void); +extern void skip(void); +extern void suffix(char*); +extern long tread(char*, long); +extern void trunc(Dir*, Dir**); +extern long tseek(long, int); +extern void twrite(char*, long); +extern void usage(void); + +#define JUMP(o,p) tseek(o,p), copy() + +void +main(int argc, char **argv) +{ + int seekable, c; + + Binit(&bout, 1, OWRITE); + for(; argc > 1 && ((c=*argv[1])=='-'||c=='+'); argc--,argv++ ) { + if(getnumber(argv[1])) { + suffix(argv[1]); + continue; + } else + if(c == '-') + switch(argv[1][1]) { + case 'c': + units = CHARS; + case 'n': + if(getnumber(argv[1]+2)) + continue; + else + if(argc > 2 && getnumber(argv[2])) { + argc--, argv++; + continue; + } else + usage(); + case 'r': + dir = REV; + continue; + case 'f': + follow++; + continue; + case '-': + argc--, argv++; + } + break; + } + if(dir==REV && (units==CHARS || follow || origin==BEG)) + fatal("incompatible options"); + if(!anycount) + count = dir==REV? ~0UL>>1: 10; + if(origin==BEG && units==LINES && count>0) + count--; + if(argc > 2) + usage(); + if(argc > 1 && (file=open(argv[1],0)) < 0) + fatal(argv[1]); + seekable = seek(file,0L,0) == 0; + + if(!seekable && origin==END) + keep(); + else + if(!seekable && origin==BEG) + skip(); + else + if(units==CHARS && origin==END) + JUMP(-count, 2); + else + if(units==CHARS && origin==BEG) + JUMP(count, 0); + else + if(units==LINES && origin==END) + reverse(); + else + if(units==LINES && origin==BEG) + skip(); + if(follow && seekable) + for(;;) { + static Dir *sb0, *sb1; + trunc(sb1, &sb0); + copy(); + trunc(sb0, &sb1); + sleep(5000); + } + exits(0); +} + +void +trunc(Dir *old, Dir **new) +{ + Dir *d; + ulong olength; + + d = dirfstat(file); + if(d == nil) + return; + olength = 0; + if(old) + olength = old->length; + if(d->length < olength) + d->length = tseek(0L, 0); + free(*new); + *new = d; +} + +void +suffix(char *s) +{ + while(*s && strchr("0123456789+-", *s)) + s++; + switch(*s) { + case 'b': + if((count *= 1024) < 0) + fatal("too big"); + case 'c': + units = CHARS; + case 'l': + s++; + } + switch(*s) { + case 'r': + dir = REV; + return; + case 'f': + follow++; + return; + case 0: + return; + } + usage(); +} + +/* + * read past head of the file to find tail + */ +void +skip(void) +{ + int i; + long n; + char buf[Bsize]; + if(units == CHARS) { + for( ; count>0; count -=n) { + n = count<Bsize? count: Bsize; + if(!(n = tread(buf, n))) + return; + } + } else /*units == LINES*/ { + n = i = 0; + while(count > 0) { + if(!(n = tread(buf, Bsize))) + return; + for(i=0; i<n && count>0; i++) + if(buf[i]=='\n') + count--; + } + twrite(buf+i, n-i); + } + copy(); +} + +void +copy(void) +{ + long n; + char buf[Bsize]; + while((n=tread(buf, Bsize)) > 0) { + twrite(buf, n); + Bflush(&bout); /* for FWD on pipe; else harmless */ + } +} + +/* + * read whole file, keeping the tail + * complexity is length(file)*length(tail). + * could be linear. + */ +void +keep(void) +{ + int len = 0; + long bufsiz = 0; + char *buf = 0; + int j, k, n; + + for(n=1; n;) { + if(len+Bsize > bufsiz) { + bufsiz += 2*Bsize; + if(!(buf = realloc(buf, bufsiz+1))) + fatal("out of space"); + } + for(; n && len<bufsiz; len+=n) + n = tread(buf+len, bufsiz-len); + if(count >= len) + continue; + if(units == CHARS) + j = len - count; + else { + /* units == LINES */ + j = buf[len-1]=='\n'? len-1: len; + for(k=0; j>0; j--) + if(buf[j-1] == '\n') + if(++k >= count) + break; + } + memmove(buf, buf+j, len-=j); + } + if(dir == REV) { + if(len>0 && buf[len-1]!='\n') + buf[len++] = '\n'; + for(j=len-1 ; j>0; j--) + if(buf[j-1] == '\n') { + twrite(buf+j, len-j); + if(--count <= 0) + return; + len = j; + } + } + if(count > 0) + twrite(buf, len); +} + +/* + * count backward and print tail of file + */ +void +reverse(void) +{ + int first; + long len = 0; + long n = 0; + long bufsiz = 0; + char *buf = 0; + long pos = tseek(0L, 2); + + for(first=1; pos>0 && count>0; first=0) { + n = pos>Bsize? Bsize: (int)pos; + pos -= n; + if(len+n > bufsiz) { + bufsiz += 2*Bsize; + if(!(buf = realloc(buf, bufsiz+1))) + fatal("out of space"); + } + memmove(buf+n, buf, len); + len += n; + tseek(pos, 0); + if(tread(buf, n) != n) + fatal("length error"); + if(first && buf[len-1]!='\n') + buf[len++] = '\n'; + for(n=len-1 ; n>0 && count>0; n--) + if(buf[n-1] == '\n') { + count--; + if(dir == REV) + twrite(buf+n, len-n); + len = n; + } + } + if(dir == FWD) { + tseek(n==0? 0 : pos+n+1, 0); + copy(); + } else + if(count > 0) + twrite(buf, len); +} + +long +tseek(long o, int p) +{ + o = seek(file, o, p); + if(o == -1) + fatal(""); + return o; +} + +long +tread(char *buf, long n) +{ + int r = read(file, buf, n); + if(r == -1) + fatal(""); + return r; +} + +void +twrite(char *s, long n) +{ + if(Bwrite(&bout, s, n) != n) + fatal(""); +} + +int +getnumber(char *s) +{ + if(*s=='-' || *s=='+') + s++; + if(!isdigit(*s)) + return 0; + if(s[-1] == '+') + origin = BEG; + if(anycount++) + fatal("excess option"); + count = atol(s); + + /* check range of count */ + if(count < 0 || (int)count != count) + fatal("too big"); + return 1; +} + +void +fatal(char *s) +{ + char buf[ERRMAX]; + + errstr(buf, sizeof buf); + fprint(2, "tail: %s: %s\n", s, buf); + exits(s); +} + +void +usage(void) +{ + fprint(2, "%s\n", umsg); + exits("usage"); +} diff --git a/src/cmd/tar.C b/src/cmd/tar.C new file mode 100644 index 00000000..38418c47 --- /dev/null +++ b/src/cmd/tar.C @@ -0,0 +1,640 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +#define TBLOCK 512 +#define NBLOCK 40 /* maximum blocksize */ +#define DBLOCK 20 /* default blocksize */ +#define NAMSIZ 100 +union hblock +{ + char dummy[TBLOCK]; + struct header + { + char name[NAMSIZ]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char chksum[8]; + char linkflag; + char linkname[NAMSIZ]; + } dbuf; +} dblock, tbuf[NBLOCK]; + +Dir *stbuf; +Biobuf bout; + +int rflag, xflag, vflag, tflag, mt, cflag, fflag, Tflag, Rflag; +int uflag, gflag; +int chksum, recno, first; +int nblock = DBLOCK; + +void usage(void); +void dorep(char **); +int endtar(void); +void getdir(void); +void passtar(void); +void putfile(char*, char *, char *); +void doxtract(char **); +void dotable(void); +void putempty(void); +void longt(Dir *); +int checkdir(char *, int, Qid*); +void tomodes(Dir *); +int checksum(void); +int checkupdate(char *); +int prefix(char *, char *); +int readtar(char *); +int writetar(char *); +void backtar(void); +void flushtar(void); +void affix(int, char *); +int volprompt(void); +void +main(int argc, char **argv) +{ + char *usefile; + char *cp, *ap; + + if (argc < 2) + usage(); + + Binit(&bout, 1, OWRITE); + usefile = 0; + argv[argc] = 0; + argv++; + for (cp = *argv++; *cp; cp++) + switch(*cp) { + case 'f': + usefile = *argv++; + if(!usefile) + usage(); + fflag++; + break; + case 'u': + ap = *argv++; + if(!ap) + usage(); + uflag = strtoul(ap, 0, 0); + break; + case 'g': + ap = *argv++; + if(!ap) + usage(); + gflag = strtoul(ap, 0, 0); + break; + case 'c': + cflag++; + rflag++; + break; + case 'r': + rflag++; + break; + case 'v': + vflag++; + break; + case 'x': + xflag++; + break; + case 'T': + Tflag++; + break; + case 't': + tflag++; + break; + case 'R': + Rflag++; + break; + case '-': + break; + default: + fprint(2, "tar: %c: unknown option\n", *cp); + usage(); + } + + fmtinstall('M', dirmodefmt); + + if (rflag) { + if (!usefile) { + if (cflag == 0) { + fprint(2, "tar: can only create standard output archives\n"); + exits("arg error"); + } + mt = dup(1, -1); + nblock = 1; + } + else if ((mt = open(usefile, ORDWR)) < 0) { + if (cflag == 0 || (mt = create(usefile, OWRITE, 0666)) < 0) { + fprint(2, "tar: cannot open %s: %r\n", usefile); + exits("open"); + } + } + dorep(argv); + } + else if (xflag) { + if (!usefile) { + mt = dup(0, -1); + nblock = 1; + } + else if ((mt = open(usefile, OREAD)) < 0) { + fprint(2, "tar: cannot open %s: %r\n", usefile); + exits("open"); + } + doxtract(argv); + } + else if (tflag) { + if (!usefile) { + mt = dup(0, -1); + nblock = 1; + } + else if ((mt = open(usefile, OREAD)) < 0) { + fprint(2, "tar: cannot open %s: %r\n", usefile); + exits("open"); + } + dotable(); + } + else + usage(); + exits(0); +} + +void +usage(void) +{ + fprint(2, "tar: usage tar {txrc}[Rvf] [tarfile] file1 file2...\n"); + exits("usage"); +} + +void +dorep(char **argv) +{ + char cwdbuf[2048], *cwd, thisdir[2048]; + char *cp, *cp2; + int cd; + + if (getwd(cwdbuf, sizeof(cwdbuf)) == 0) { + fprint(2, "tar: can't find current directory: %r\n"); + exits("cwd"); + } + cwd = cwdbuf; + + if (!cflag) { + getdir(); + do { + passtar(); + getdir(); + } while (!endtar()); + } + + while (*argv) { + cp2 = *argv; + if (!strcmp(cp2, "-C") && argv[1]) { + argv++; + if (chdir(*argv) < 0) + perror(*argv); + cwd = *argv; + argv++; + continue; + } + cd = 0; + for (cp = *argv; *cp; cp++) + if (*cp == '/') + cp2 = cp; + if (cp2 != *argv) { + *cp2 = '\0'; + chdir(*argv); + if(**argv == '/') + strncpy(thisdir, *argv, sizeof(thisdir)); + else + snprint(thisdir, sizeof(thisdir), "%s/%s", cwd, *argv); + *cp2 = '/'; + cp2++; + cd = 1; + } else + strncpy(thisdir, cwd, sizeof(thisdir)); + putfile(thisdir, *argv++, cp2); + if(cd && chdir(cwd) < 0) { + fprint(2, "tar: can't cd back to %s: %r\n", cwd); + exits("cwd"); + } + } + putempty(); + putempty(); + flushtar(); +} + +int +endtar(void) +{ + if (dblock.dbuf.name[0] == '\0') { + backtar(); + return(1); + } + else + return(0); +} + +void +getdir(void) +{ + Dir *sp; + + readtar((char*)&dblock); + if (dblock.dbuf.name[0] == '\0') + return; + if(stbuf == nil){ + stbuf = malloc(sizeof(Dir)); + if(stbuf == nil) { + fprint(2, "tar: can't malloc: %r\n"); + exits("malloc"); + } + } + sp = stbuf; + sp->mode = strtol(dblock.dbuf.mode, 0, 8); + sp->uid = "adm"; + sp->gid = "adm"; + sp->length = strtol(dblock.dbuf.size, 0, 8); + sp->mtime = strtol(dblock.dbuf.mtime, 0, 8); + chksum = strtol(dblock.dbuf.chksum, 0, 8); + if (chksum != checksum()) { + fprint(2, "directory checksum error\n"); + exits("checksum error"); + } + sp->qid.type = 0; + /* the mode test is ugly but sometimes necessary */ + if (dblock.dbuf.linkflag == '5' || (sp->mode&0170000) == 040000) { + sp->qid.type |= QTDIR; + sp->mode |= DMDIR; + } +} + +void +passtar(void) +{ + long blocks; + char buf[TBLOCK]; + + if (dblock.dbuf.linkflag == '1' || dblock.dbuf.linkflag == 's') + return; + blocks = stbuf->length; + blocks += TBLOCK-1; + blocks /= TBLOCK; + + while (blocks-- > 0) + readtar(buf); +} + +void +putfile(char *dir, char *longname, char *sname) +{ + int infile; + long blocks; + char buf[TBLOCK]; + char curdir[4096]; + char shortname[4096]; + char *cp, *cp2; + Dir *db; + int i, n; + + if(strlen(sname) > sizeof shortname - 3){ + fprint(2, "tar: %s: name too long (max %d)\n", sname, sizeof shortname - 3); + return; + } + + snprint(shortname, sizeof shortname, "./%s", sname); + infile = open(shortname, OREAD); + if (infile < 0) { + fprint(2, "tar: %s: cannot open file - %r\n", longname); + return; + } + + if(stbuf != nil) + free(stbuf); + stbuf = dirfstat(infile); + + if (stbuf->qid.type & QTDIR) { + /* Directory */ + for (i = 0, cp = buf; *cp++ = longname[i++];); + *--cp = '/'; + *++cp = 0; + if( (cp - buf) >= NAMSIZ) { + fprint(2, "tar: %s: file name too long\n", longname); + close(infile); + return; + } + stbuf->length = 0; + tomodes(stbuf); + strcpy(dblock.dbuf.name,buf); + dblock.dbuf.linkflag = '5'; /* Directory */ + sprint(dblock.dbuf.chksum, "%6o", checksum()); + writetar( (char *) &dblock); + if (chdir(shortname) < 0) { + fprint(2, "tar: can't cd to %s: %r\n", shortname); + snprint(curdir, sizeof(curdir), "cd %s", shortname); + exits(curdir); + } + sprint(curdir, "%s/%s", dir, sname); + while ((n = dirread(infile, &db)) > 0) { + for(i = 0; i < n; i++){ + strncpy(cp, db[i].name, sizeof buf - (cp-buf)); + putfile(curdir, buf, db[i].name); + }free(db); + } + close(infile); + if (chdir(dir) < 0 && chdir("..") < 0) { + fprint(2, "tar: can't cd to ..(%s): %r\n", dir); + snprint(curdir, sizeof(curdir), "cd ..(%s)", dir); + exits(curdir); + } + return; + } + + + tomodes(stbuf); + + cp2 = longname; + for (cp = dblock.dbuf.name, i=0; (*cp++ = *cp2++) && i < NAMSIZ; i++); + if (i >= NAMSIZ) { + fprint(2, "%s: file name too long\n", longname); + close(infile); + return; + } + + blocks = (stbuf->length + (TBLOCK-1)) / TBLOCK; + if (vflag) { + fprint(2, "a %s ", longname); + fprint(2, "%ld blocks\n", blocks); + } + dblock.dbuf.linkflag = 0; /* Regular file */ + sprint(dblock.dbuf.chksum, "%6o", checksum()); + writetar( (char *) &dblock); + + while ((i = readn(infile, buf, TBLOCK)) > 0 && blocks > 0) { + writetar(buf); + blocks--; + } + close(infile); + if (blocks != 0 || i != 0) + fprint(2, "%s: file changed size\n", longname); + while (blocks-- > 0) + putempty(); +} + + +void +doxtract(char **argv) +{ + Dir null; + long blocks, bytes; + char buf[TBLOCK], outname[NAMSIZ+4]; + char **cp; + int ofile; + + for (;;) { + getdir(); + if (endtar()) + break; + + if (*argv == 0) + goto gotit; + + for (cp = argv; *cp; cp++) + if (prefix(*cp, dblock.dbuf.name)) + goto gotit; + passtar(); + continue; + +gotit: + if(checkdir(dblock.dbuf.name, stbuf->mode, &(stbuf->qid))) + continue; + + if (dblock.dbuf.linkflag == '1') { + fprint(2, "tar: can't link %s %s\n", + dblock.dbuf.linkname, dblock.dbuf.name); + remove(dblock.dbuf.name); + continue; + } + if (dblock.dbuf.linkflag == 's') { + fprint(2, "tar: %s: cannot symlink\n", dblock.dbuf.name); + continue; + } + if(dblock.dbuf.name[0] != '/' || Rflag) + sprint(outname, "./%s", dblock.dbuf.name); + else + strcpy(outname, dblock.dbuf.name); + if ((ofile = create(outname, OWRITE, stbuf->mode & 0777)) < 0) { + fprint(2, "tar: %s - cannot create: %r\n", outname); + passtar(); + continue; + } + + blocks = ((bytes = stbuf->length) + TBLOCK-1)/TBLOCK; + if (vflag) + fprint(2, "x %s, %ld bytes\n", + dblock.dbuf.name, bytes); + while (blocks-- > 0) { + readtar(buf); + if (bytes > TBLOCK) { + if (write(ofile, buf, TBLOCK) < 0) { + fprint(2, "tar: %s: HELP - extract write error: %r\n", dblock.dbuf.name); + exits("extract write"); + } + } else + if (write(ofile, buf, bytes) < 0) { + fprint(2, "tar: %s: HELP - extract write error: %r\n", dblock.dbuf.name); + exits("extract write"); + } + bytes -= TBLOCK; + } + if(Tflag){ + nulldir(&null); + null.mtime = stbuf->mtime; + dirfwstat(ofile, &null); + } + close(ofile); + } +} + +void +dotable(void) +{ + for (;;) { + getdir(); + if (endtar()) + break; + if (vflag) + longt(stbuf); + Bprint(&bout, "%s", dblock.dbuf.name); + if (dblock.dbuf.linkflag == '1') + Bprint(&bout, " linked to %s", dblock.dbuf.linkname); + if (dblock.dbuf.linkflag == 's') + Bprint(&bout, " -> %s", dblock.dbuf.linkname); + Bprint(&bout, "\n"); + passtar(); + } +} + +void +putempty(void) +{ + char buf[TBLOCK]; + + memset(buf, 0, TBLOCK); + writetar(buf); +} + +void +longt(Dir *st) +{ + char *cp; + + Bprint(&bout, "%M %4d/%1d ", st->mode, 0, 0); /* 0/0 uid/gid */ + Bprint(&bout, "%8lld", st->length); + cp = ctime(st->mtime); + Bprint(&bout, " %-12.12s %-4.4s ", cp+4, cp+24); +} + +int +checkdir(char *name, int mode, Qid *qid) +{ + char *cp; + int f; + Dir *d, null; + + if(Rflag && *name == '/') + name++; + cp = name; + if(*cp == '/') + cp++; + for (; *cp; cp++) { + if (*cp == '/') { + *cp = '\0'; + if (access(name, 0) < 0) { + f = create(name, OREAD, DMDIR + 0775L); + if(f < 0) + fprint(2, "tar: mkdir %s failed: %r\n", name); + close(f); + } + *cp = '/'; + } + } + + /* if this is a directory, chmod it to the mode in the tar plus 700 */ + if(cp[-1] == '/' || (qid->type&QTDIR)){ + if((d=dirstat(name)) != 0){ + nulldir(&null); + null.mode = DMDIR | (mode & 0777) | 0700; + dirwstat(name, &null); + free(d); + } + return 1; + } else + return 0; +} + +void +tomodes(Dir *sp) +{ + char *cp; + + for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++) + *cp = '\0'; + sprint(dblock.dbuf.mode, "%6lo ", sp->mode & 0777); + sprint(dblock.dbuf.uid, "%6o ", uflag); + sprint(dblock.dbuf.gid, "%6o ", gflag); + sprint(dblock.dbuf.size, "%11llo ", sp->length); + sprint(dblock.dbuf.mtime, "%11lo ", sp->mtime); +} + +int +checksum(void) +{ + int i; + char *cp; + + for (cp = dblock.dbuf.chksum; cp < &dblock.dbuf.chksum[sizeof(dblock.dbuf.chksum)]; cp++) + *cp = ' '; + i = 0; + for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++) + i += *cp & 0xff; + return(i); +} + +int +prefix(char *s1, char *s2) +{ + while (*s1) + if (*s1++ != *s2++) + return(0); + if (*s2) + return(*s2 == '/'); + return(1); +} + +int +readtar(char *buffer) +{ + int i; + + if (recno >= nblock || first == 0) { + if ((i = readn(mt, tbuf, TBLOCK*nblock)) <= 0) { + fprint(2, "tar: archive read error: %r\n"); + exits("archive read"); + } + if (first == 0) { + if ((i % TBLOCK) != 0) { + fprint(2, "tar: archive blocksize error: %r\n"); + exits("blocksize"); + } + i /= TBLOCK; + if (i != nblock) { + fprint(2, "tar: blocksize = %d\n", i); + nblock = i; + } + } + recno = 0; + } + first = 1; + memmove(buffer, &tbuf[recno++], TBLOCK); + return(TBLOCK); +} + +int +writetar(char *buffer) +{ + first = 1; + if (recno >= nblock) { + if (write(mt, tbuf, TBLOCK*nblock) != TBLOCK*nblock) { + fprint(2, "tar: archive write error: %r\n"); + exits("write"); + } + recno = 0; + } + memmove(&tbuf[recno++], buffer, TBLOCK); + if (recno >= nblock) { + if (write(mt, tbuf, TBLOCK*nblock) != TBLOCK*nblock) { + fprint(2, "tar: archive write error: %r\n"); + exits("write"); + } + recno = 0; + } + return(TBLOCK); +} + +/* + * backup over last tar block + */ +void +backtar(void) +{ + seek(mt, -TBLOCK*nblock, 1); + recno--; +} + +void +flushtar(void) +{ + write(mt, tbuf, TBLOCK*nblock); +} diff --git a/src/cmd/tee.c b/src/cmd/tee.c new file mode 100644 index 00000000..83db8a08 --- /dev/null +++ b/src/cmd/tee.c @@ -0,0 +1,75 @@ +/* + * tee-- pipe fitting + */ + +#include <u.h> +#include <libc.h> + +int uflag; +int aflag; +int openf[100]; + +char in[8192]; + +int intignore(void*, char*); + +void +main(int argc, char **argv) +{ + int i; + int r, n; + + ARGBEGIN { + case 'a': + aflag++; + break; + + case 'i': + atnotify(intignore, 1); + break; + + case 'u': + uflag++; + /* uflag is ignored and undocumented; it's a relic from Unix */ + break; + + default: + fprint(2, "usage: tee [-ai] [file ...]\n"); + exits("usage"); + } ARGEND + + USED(argc); + n = 0; + while(*argv) { + if(aflag) { + openf[n] = open(argv[0], OWRITE); + if(openf[n] < 0) + openf[n] = create(argv[0], OWRITE, 0666); + seek(openf[n], 0L, 2); + } else + openf[n] = create(argv[0], OWRITE, 0666); + if(openf[n] < 0) { + fprint(2, "tee: cannot open %s: %r\n", argv[0]); + } else + n++; + argv++; + } + openf[n++] = 1; + + for(;;) { + r = read(0, in, sizeof in); + if(r <= 0) + exits(nil); + for(i=0; i<n; i++) + write(openf[i], in, r); + } +} + +int +intignore(void *a, char *msg) +{ + USED(a); + if(strcmp(msg, "interrupt") == 0) + return 1; + return 0; +} diff --git a/src/cmd/test.c b/src/cmd/test.c new file mode 100644 index 00000000..0282f198 --- /dev/null +++ b/src/cmd/test.c @@ -0,0 +1,303 @@ +/* + * POSIX standard + * test expression + * [ expression ] + * + * Plan 9 additions: + * -A file exists and is append-only + * -L file exists and is exclusive-use + */ + +#include <u.h> +#include <libc.h> +#define EQ(a,b) ((tmp=a)==0?0:(strcmp(tmp,b)==0)) + +int ap; +int ac; +char **av; +char *tmp; + +void synbad(char *, char *); +int fsizep(char *); +int isdir(char *); +int isreg(char *); +int isatty(int); +int isint(char *, int *); +int hasmode(char *, ulong); +int tio(char *, int); +int e(void), e1(void), e2(void), e3(void); + +void +main(int argc, char *argv[]) +{ + + ac = argc; av = argv; ap = 1; + if(EQ(argv[0],"[")) { + if(!EQ(argv[--ac],"]")) + synbad("] missing",""); + } + argv[ac] = 0; + if (ac<=1) exits("usage"); + exits(e()?0:"false"); +} + +char * +nxtarg(int mt) +{ + if(ap>=ac){ + if(mt){ + ap++; + return(0); + } + synbad("argument expected",""); + } + return(av[ap++]); +} + +int +nxtintarg(int *pans) +{ + if(ap<ac && isint(av[ap], pans)){ + ap++; + return 1; + } + return 0; +} + +int +e(void) { + int p1; + + p1 = e1(); + if (EQ(nxtarg(1), "-o")) return(p1 || e()); + ap--; + return(p1); +} + +int +e1(void) { + int p1; + + p1 = e2(); + if (EQ(nxtarg(1), "-a")) return (p1 && e1()); + ap--; + return(p1); +} + +int +e2(void) { + if (EQ(nxtarg(0), "!")) + return(!e2()); + ap--; + return(e3()); +} + +int +e3(void) { + int p1; + char *a; + char *p2; + int int1, int2; + + a = nxtarg(0); + if(EQ(a, "(")) { + p1 = e(); + if(!EQ(nxtarg(0), ")")) synbad(") expected",""); + return(p1); + } + + if(EQ(a, "-A")) + return(hasmode(nxtarg(0), DMAPPEND)); + + if(EQ(a, "-L")) + return(hasmode(nxtarg(0), DMEXCL)); + + if(EQ(a, "-f")) + return(isreg(nxtarg(0))); + + if(EQ(a, "-d")) + return(isdir(nxtarg(0))); + + if(EQ(a, "-r")) + return(tio(nxtarg(0), 4)); + + if(EQ(a, "-w")) + return(tio(nxtarg(0), 2)); + + if(EQ(a, "-x")) + return(tio(nxtarg(0), 1)); + + if(EQ(a, "-e")) + return(tio(nxtarg(0), 0)); + + if(EQ(a, "-c")) + return(0); + + if(EQ(a, "-b")) + return(0); + + if(EQ(a, "-u")) + return(0); + + if(EQ(a, "-g")) + return(0); + + if(EQ(a, "-s")) + return(fsizep(nxtarg(0))); + + if(EQ(a, "-t")) + if(ap>=ac || !nxtintarg(&int1)) + return(isatty(1)); + else + return(isatty(int1)); + + if(EQ(a, "-n")) + return(!EQ(nxtarg(0), "")); + if(EQ(a, "-z")) + return(EQ(nxtarg(0), "")); + + p2 = nxtarg(1); + if (p2==0) + return(!EQ(a,"")); + if(EQ(p2, "=")) + return(EQ(nxtarg(0), a)); + + if(EQ(p2, "!=")) + return(!EQ(nxtarg(0), a)); + + if(!isint(a, &int1)) + return(!EQ(a,"")); + + if(nxtintarg(&int2)){ + if(EQ(p2, "-eq")) + return(int1==int2); + if(EQ(p2, "-ne")) + return(int1!=int2); + if(EQ(p2, "-gt")) + return(int1>int2); + if(EQ(p2, "-lt")) + return(int1<int2); + if(EQ(p2, "-ge")) + return(int1>=int2); + if(EQ(p2, "-le")) + return(int1<=int2); + } + + synbad("unknown operator ",p2); + return 0; /* to shut ken up */ +} + +int +tio(char *a, int f) +{ + return access (a, f) >= 0; +} + +/* copy to local memory; clear names for safety */ +int +localstat(char *f, Dir *dir) +{ + Dir *d; + + d = dirstat(f); + if(d == 0) + return(-1); + *dir = *d; + dir->name = 0; + dir->uid = 0; + dir->gid = 0; + dir->muid = 0; + return 0; +} + +/* copy to local memory; clear names for safety */ +int +localfstat(int f, Dir *dir) +{ + Dir *d; + + d = dirfstat(f); + if(d == 0) + return(-1); + *dir = *d; + dir->name = 0; + dir->uid = 0; + dir->gid = 0; + dir->muid = 0; + return 0; +} + +int +hasmode(char *f, ulong m) +{ + Dir dir; + + if(localstat(f,&dir)<0) + return(0); + return(dir.mode&m); +} + +int +isdir(char *f) +{ + Dir dir; + + if(localstat(f,&dir)<0) + return(0); + return(dir.mode&DMDIR); +} + +int +isreg(char *f) +{ + Dir dir; + + if(localstat(f,&dir)<0) + return(0); + return(!(dir.mode&DMDIR)); +} + +int +isatty(int fd) +{ + Dir d1, d2; + + if(localfstat(fd, &d1) < 0) + return 0; + if(localstat("/dev/cons", &d2) < 0) + return 0; + return d1.type==d2.type && d1.dev==d2.dev && d1.qid.path==d2.qid.path; +} + +int +fsizep(char *f) +{ + Dir dir; + + if(localstat(f,&dir)<0) + return(0); + return(dir.length>0); +} + +void +synbad(char *s1, char *s2) +{ + int len; + + write(2, "test: ", 6); + if ((len = strlen(s1)) != 0) + write(2, s1, len); + if ((len = strlen(s2)) != 0) + write(2, s2, len); + write(2, "\n", 1); + exits("bad syntax"); +} + +int +isint(char *s, int *pans) +{ + char *ep; + + *pans = strtol(s, &ep, 0); + return (*ep == 0); +} diff --git a/src/cmd/time.c b/src/cmd/time.c new file mode 100644 index 00000000..d82eaa94 --- /dev/null +++ b/src/cmd/time.c @@ -0,0 +1,101 @@ +#include <u.h> +#include <libc.h> + +char output[4096]; +void add(char*, ...); +void error(char*); +void notifyf(void*, char*); + +void +main(int argc, char *argv[]) +{ + int i; + Waitmsg *w; + long l; + char *p; + char err[ERRMAX]; + + if(argc <= 1){ + fprint(2, "usage: time command\n"); + exits("usage"); + } + + switch(fork()){ + case -1: + error("fork"); + case 0: + exec(argv[1], &argv[1]); + if(argv[1][0] != '/' && strncmp(argv[1], "./", 2) && + strncmp(argv[1], "../", 3)){ + sprint(output, "/bin/%s", argv[1]); + exec(output, &argv[1]); + } + error(argv[1]); + } + + notify(notifyf); + + loop: + w = wait(); + if(w == nil){ + errstr(err, sizeof err); + if(strcmp(err, "interrupted") == 0) + goto loop; + error("wait"); + } + l = w->time[0]; + add("%ld.%.2ldu", l/1000, (l%1000)/10); + l = w->time[1]; + add("%ld.%.2lds", l/1000, (l%1000)/10); + l = w->time[2]; + add("%ld.%.2ldr", l/1000, (l%1000)/10); + add("\t"); + for(i=1; i<argc; i++){ + add("%s", argv[i], 0); + if(i>4){ + add("..."); + break; + } + } + if(w->msg[0]){ + p = utfrune(w->msg, ':'); + if(p && p[1]) + p++; + else + p = w->msg; + add(" # status=%s", p); + } + fprint(2, "%s\n", output); + exits(w->msg); +} + +void +add(char *a, ...) +{ + static int beenhere=0; + va_list arg; + + if(beenhere) + strcat(output, " "); + va_start(arg, a); + vseprint(output+strlen(output), output+sizeof(output), a, arg); + va_end(arg); + beenhere++; +} + +void +error(char *s) +{ + + fprint(2, "time: %s: %r\n", s); + exits(s); +} + +void +notifyf(void *a, char *s) +{ + USED(a); + if(strcmp(s, "interrupt") == 0) + noted(NCONT); + noted(NDFLT); +} diff --git a/src/cmd/touch.c b/src/cmd/touch.c new file mode 100644 index 00000000..539f89d2 --- /dev/null +++ b/src/cmd/touch.c @@ -0,0 +1,62 @@ +#include <u.h> +#include <libc.h> + +int touch(int, char *); +ulong now; + +void +usage(void) +{ + fprint(2, "usage: touch [-c] [-t time] files\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int nocreate = 0; + int status = 0; + + now = time(0); + ARGBEGIN{ + case 't': + now = strtoul(EARGF(usage()), 0, 0); + break; + case 'c': + nocreate = 1; + break; + default: + usage(); + }ARGEND + + if(!*argv) + usage(); + while(*argv) + status += touch(nocreate, *argv++); + if(status) + exits("touch"); + exits(0); +} + +int +touch(int nocreate, char *name) +{ + Dir stbuff; + int fd; + + nulldir(&stbuff); + stbuff.mtime = now; + if(dirwstat(name, &stbuff) >= 0) + return 0; + if(nocreate){ + fprint(2, "touch: %s: cannot wstat: %r\n", name); + return 1; + } + if ((fd = create(name, OREAD, 0666)) < 0) { + fprint(2, "touch: %s: cannot create: %r\n", name); + return 1; + } + dirfwstat(fd, &stbuff); + close(fd); + return 0; +} diff --git a/src/cmd/tr.c b/src/cmd/tr.c new file mode 100644 index 00000000..39ba747c --- /dev/null +++ b/src/cmd/tr.c @@ -0,0 +1,356 @@ +#include <u.h> +#include <libc.h> + +typedef struct PCB /* Control block controlling specification parse */ +{ + char *base; /* start of specification */ + char *current; /* current parse point */ + long last; /* last Rune returned */ + long final; /* final Rune in a span */ +} Pcb; + +uchar bits[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; + +#define SETBIT(a, c) ((a)[(c)/8] |= bits[(c)&07]) +#define CLEARBIT(a,c) ((a)[(c)/8] &= ~bits[(c)&07]) +#define BITSET(a,c) ((a)[(c)/8] & bits[(c)&07]) + +#define MAXRUNE 0xFFFF + +uchar f[(MAXRUNE+1)/8]; +uchar t[(MAXRUNE+1)/8]; +char wbuf[4096]; +char *wptr; + +Pcb pfrom, pto; + +int cflag; +int dflag; +int sflag; + +void complement(void); +void delete(void); +void squeeze(void); +void translit(void); +void error(char*); +long canon(Pcb*); +char *getrune(char*, Rune*); +void Pinit(Pcb*, char*); +void Prewind(Pcb *p); +int readrune(int, long*); +void wflush(int); +void writerune(int, Rune); + +void +main(int argc, char **argv) +{ + ARGBEGIN{ + case 's': sflag++; break; + case 'd': dflag++; break; + case 'c': cflag++; break; + default: error("bad option"); + }ARGEND + if(argc>0) + Pinit(&pfrom, argv[0]); + if(argc>1) + Pinit(&pto, argv[1]); + if(argc>2) + error("arg count"); + if(dflag) { + if ((sflag && argc != 2) || (!sflag && argc != 1)) + error("arg count"); + delete(); + } else { + if (argc != 2) + error("arg count"); + if (cflag) + complement(); + else translit(); + } + exits(0); +} + +void +delete(void) +{ + long c, last; + + if (cflag) { + memset((char *) f, 0xff, sizeof f); + while ((c = canon(&pfrom)) >= 0) + CLEARBIT(f, c); + } else { + while ((c = canon(&pfrom)) >= 0) + SETBIT(f, c); + } + if (sflag) { + while ((c = canon(&pto)) >= 0) + SETBIT(t, c); + } + + last = 0x10000; + while (readrune(0, &c) > 0) { + if(!BITSET(f, c) && (c != last || !BITSET(t,c))) { + last = c; + writerune(1, (Rune) c); + } + } + wflush(1); +} + +void +complement(void) +{ + Rune *p; + int i; + long from, to, lastc, high; + + lastc = 0; + high = 0; + while ((from = canon(&pfrom)) >= 0) { + if (from > high) high = from; + SETBIT(f, from); + } + while ((to = canon(&pto)) > 0) { + if (to > high) high = to; + SETBIT(t,to); + } + Prewind(&pto); + if ((p = (Rune *) malloc((high+1)*sizeof(Rune))) == 0) + error("can't allocate memory"); + for (i = 0; i <= high; i++){ + if (!BITSET(f,i)) { + if ((to = canon(&pto)) < 0) + to = lastc; + else lastc = to; + p[i] = to; + } + else p[i] = i; + } + if (sflag){ + lastc = 0x10000; + while (readrune(0, &from) > 0) { + if (from > high) + from = to; + else + from = p[from]; + if (from != lastc || !BITSET(t,from)) { + lastc = from; + writerune(1, (Rune) from); + } + } + + } else { + while (readrune(0, &from) > 0){ + if (from > high) + from = to; + else + from = p[from]; + writerune(1, (Rune) from); + } + } + wflush(1); +} + +void +translit(void) +{ + Rune *p; + int i; + long from, to, lastc, high; + + lastc = 0; + high = 0; + while ((from = canon(&pfrom)) >= 0) + if (from > high) high = from; + Prewind(&pfrom); + if ((p = (Rune *) malloc((high+1)*sizeof(Rune))) == 0) + error("can't allocate memory"); + for (i = 0; i <= high; i++) + p[i] = i; + while ((from = canon(&pfrom)) >= 0) { + if ((to = canon(&pto)) < 0) + to = lastc; + else lastc = to; + if (BITSET(f,from) && p[from] != to) + error("ambiguous translation"); + SETBIT(f,from); + p[from] = to; + SETBIT(t,to); + } + while ((to = canon(&pto)) >= 0) { + SETBIT(t,to); + } + if (sflag){ + lastc = 0x10000; + while (readrune(0, &from) > 0) { + if (from <= high) + from = p[from]; + if (from != lastc || !BITSET(t,from)) { + lastc = from; + writerune(1, (Rune) from); + } + } + + } else { + while (readrune(0, &from) > 0) { + if (from <= high) + from = p[from]; + writerune(1, (Rune) from); + } + } + wflush(1); +} + +int +readrune(int fd, long *rp) +{ + Rune r; + int j; + static int i, n; + static char buf[4096]; + + j = i; + for (;;) { + if (i >= n) { + wflush(1); + if (j != i) + memcpy(buf, buf+j, n-j); + i = n-j; + n = read(fd, &buf[i], sizeof(buf)-i); + if (n < 0) + error("read error"); + if (n == 0) + return 0; + j = 0; + n += i; + } + i++; + if (fullrune(&buf[j], i-j)) + break; + } + chartorune(&r, &buf[j]); + *rp = r; + return 1; +} + +void +writerune(int fd, Rune r) +{ + char buf[UTFmax]; + int n; + + if (!wptr) + wptr = wbuf; + n = runetochar(buf, (Rune*)&r); + if (wptr+n >= wbuf+sizeof(wbuf)) + wflush(fd); + memcpy(wptr, buf, n); + wptr += n; +} + +void +wflush(int fd) +{ + if (wptr && wptr > wbuf) + if (write(fd, wbuf, wptr-wbuf) != wptr-wbuf) + error("write error"); + wptr = wbuf; +} + +char * +getrune(char *s, Rune *rp) +{ + Rune r; + char *save; + int i, n; + + s += chartorune(rp, s); + if((r = *rp) == '\\' && *s){ + n = 0; + if (*s == 'x') { + s++; + for (i = 0; i < 4; i++) { + save = s; + s += chartorune(&r, s); + if ('0' <= r && r <= '9') + n = 16*n + r - '0'; + else if ('a' <= r && r <= 'f') + n = 16*n + r - 'a' + 10; + else if ('A' <= r && r <= 'F') + n = 16*n + r - 'A' + 10; + else { + if (i == 0) + *rp = 'x'; + else *rp = n; + return save; + } + } + } else { + for(i = 0; i < 3; i++) { + save = s; + s += chartorune(&r, s); + if('0' <= r && r <= '7') + n = 8*n + r - '0'; + else { + if (i == 0) + { + *rp = r; + return s; + } + *rp = n; + return save; + } + } + if(n > 0377) + error("char>0377"); + } + *rp = n; + } + return s; +} + +long +canon(Pcb *p) +{ + Rune r; + + if (p->final >= 0) { + if (p->last < p->final) + return ++p->last; + p->final = -1; + } + if (*p->current == '\0') + return -1; + if(*p->current == '-' && p->last >= 0 && p->current[1]){ + p->current = getrune(p->current+1, &r); + if (r < p->last) + error ("Invalid range specification"); + if (r > p->last) { + p->final = r; + return ++p->last; + } + } + p->current = getrune(p->current, &r); + p->last = r; + return p->last; +} + +void +Pinit(Pcb *p, char *cp) +{ + p->current = p->base = cp; + p->last = p->final = -1; +} +void +Prewind(Pcb *p) +{ + p->current = p->base; + p->last = p->final = -1; +} +void +error(char *s) +{ + fprint(2, "%s: %s\n", argv0, s); + exits(s); +} diff --git a/src/cmd/unicode.c b/src/cmd/unicode.c new file mode 100644 index 00000000..a0447271 --- /dev/null +++ b/src/cmd/unicode.c @@ -0,0 +1,122 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +char usage[] = "unicode { [-t] hex hex ... | hexmin-hexmax ... | [-n] char ... }"; +char hex[] = "0123456789abcdefABCDEF"; +int numout = 0; +int text = 0; +char *err; +Biobuf bout; + +char *range(char*[]); +char *nums(char*[]); +char *chars(char*[]); + +void +main(int argc, char *argv[]) +{ + ARGBEGIN{ + case 'n': + numout = 1; + break; + case 't': + text = 1; + break; + }ARGEND + Binit(&bout, 1, OWRITE); + if(argc == 0){ + fprint(2, "usage: %s\n", usage); + exits("usage"); + } + if(!numout && utfrune(argv[0], '-')) + exits(range(argv)); + if(numout || strchr(hex, argv[0][0])==0) + exits(nums(argv)); + exits(chars(argv)); +} + +char* +range(char *argv[]) +{ + char *q; + int min, max; + int i; + + while(*argv){ + q = *argv; + if(strchr(hex, q[0]) == 0){ + err: + fprint(2, "unicode: bad range %s\n", *argv); + return "bad range"; + } + min = strtoul(q, &q, 16); + if(min<0 || min>0xFFFF || *q!='-') + goto err; + q++; + if(strchr(hex, *q) == 0) + goto err; + max = strtoul(q, &q, 16); + if(max<0 || max>0xFFFF || max<min || *q!=0) + goto err; + i = 0; + do{ + Bprint(&bout, "%.4x %C", min, min); + i++; + if(min==max || (i&7)==0) + Bprint(&bout, "\n"); + else + Bprint(&bout, "\t"); + min++; + }while(min<=max); + argv++; + } + return 0; +} + +char* +nums(char *argv[]) +{ + char *q; + Rune r; + int w; + + while(*argv){ + q = *argv; + while(*q){ + w = chartorune(&r, q); + if(r==0x80 && (q[0]&0xFF)!=0x80){ + fprint(2, "unicode: invalid utf string %s\n", *argv); + return "bad utf"; + } + Bprint(&bout, "%.4x\n", r); + q += w; + } + argv++; + } + return 0; +} + +char* +chars(char *argv[]) +{ + char *q; + int m; + + while(*argv){ + q = *argv; + if(strchr(hex, q[0]) == 0){ + err: + fprint(2, "unicode: bad unicode value %s\n", *argv); + return "bad char"; + } + m = strtoul(q, &q, 16); + if(m<0 || m>0xFFFF || *q!=0) + goto err; + Bprint(&bout, "%C", m); + if(!text) + Bprint(&bout, "\n"); + argv++; + } + return 0; +} diff --git a/src/cmd/uniq.c b/src/cmd/uniq.c new file mode 100644 index 00000000..122fb5ed --- /dev/null +++ b/src/cmd/uniq.c @@ -0,0 +1,169 @@ +/* + * Deal with duplicated lines in a file + */ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <ctype.h> + +#define SIZE 8000 + +int fields = 0; +int letters = 0; +int linec = 0; +char mode; +int uniq; +char *b1, *b2; +long bsize; +Biobuf fin; +Biobuf fout; + +int gline(char *buf); +void pline(char *buf); +int equal(char *b1, char *b2); +char* skip(char *s); + +void +main(int argc, char *argv[]) +{ + int f; + + bsize = SIZE; + b1 = malloc(bsize); + b2 = malloc(bsize); + f = 0; + while(argc > 1) { + if(*argv[1] == '-') { + if(isdigit(argv[1][1])) + fields = atoi(&argv[1][1]); + else + mode = argv[1][1]; + argc--; + argv++; + continue; + } + if(*argv[1] == '+') { + letters = atoi(&argv[1][1]); + argc--; + argv++; + continue; + } + f = open(argv[1], 0); + if(f < 0) { + fprint(2, "cannot open %s\n", argv[1]); + exits("open"); + } + break; + } + if(argc > 2) { + fprint(2, "unexpected argument %s\n", argv[2]); + exits("arg"); + } + Binit(&fin, f, OREAD); + Binit(&fout, 1, OWRITE); + + if(gline(b1)) + exits(0); + for(;;) { + linec++; + if(gline(b2)) { + pline(b1); + exits(0); + } + if(!equal(b1, b2)) { + pline(b1); + linec = 0; + do { + linec++; + if(gline(b1)) { + pline(b2); + exits(0); + } + } while(equal(b2, b1)); + pline(b2); + linec = 0; + } + } +} + +int +gline(char *buf) +{ + char *p; + + p = Brdline(&fin, '\n'); + if(p == 0) + return 1; + if(fin.rdline >= bsize-1) { + fprint(2, "line too long\n"); + exits("too long"); + } + memmove(buf, p, fin.rdline); + buf[fin.rdline-1] = 0; + return 0; +} + +void +pline(char *buf) +{ + + switch(mode) { + + case 'u': + if(uniq) { + uniq = 0; + return; + } + break; + + case 'd': + if(uniq) + break; + return; + + case 'c': + Bprint(&fout, "%4d ", linec); + } + uniq = 0; + Bprint(&fout, "%s\n", buf); +} + +int +equal(char *b1, char *b2) +{ + char c; + + if(fields || letters) { + b1 = skip(b1); + b2 = skip(b2); + } + for(;;) { + c = *b1++; + if(c != *b2++) { + if(c == 0 && mode == 's') + return 1; + return 0; + } + if(c == 0) { + uniq++; + return 1; + } + } +} + +char* +skip(char *s) +{ + int nf, nl; + + nf = nl = 0; + while(nf++ < fields) { + while(*s == ' ' || *s == '\t') + s++; + while(!(*s == ' ' || *s == '\t' || *s == 0) ) + s++; + } + while(nl++ < letters && *s != 0) + s++; + return s; +} diff --git a/src/cmd/unutf.c b/src/cmd/unutf.c new file mode 100644 index 00000000..c1617315 --- /dev/null +++ b/src/cmd/unutf.c @@ -0,0 +1,16 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +Biobuf bin; + +void +main(void) +{ + int c; + + Binit(&bin, 0, OREAD); + while((c = Bgetrune(&bin)) >= 0) + print("0x%ux\n", c); + exits(0); +} diff --git a/src/cmd/wc.c b/src/cmd/wc.c new file mode 100644 index 00000000..83940880 --- /dev/null +++ b/src/cmd/wc.c @@ -0,0 +1,309 @@ +/* + * wc -- count things in utf-encoded text files + * Bugs: + * The only white space characters recognized are ' ', '\t' and '\n', even though + * ISO 10646 has many more blanks scattered through it. + * Should count characters that cannot occur in any rune (hex f0-ff) separately. + * Should count non-canonical runes (e.g. hex c1,80 instead of hex 40). + */ +#include <u.h> +#include <libc.h> +#define NBUF (8*1024) +uvlong nline, tnline, pline; +uvlong nword, tnword, pword; +uvlong nrune, tnrune, prune; +uvlong nbadr, tnbadr, pbadr; +uvlong nchar, tnchar, pchar; +void count(int, char *); +void report(uvlong, uvlong, uvlong, uvlong, uvlong, char *); +void +main(int argc, char *argv[]) +{ + char *status=""; + int i, f; + ARGBEGIN { + case 'l': pline++; break; + case 'w': pword++; break; + case 'r': prune++; break; + case 'b': pbadr++; break; + case 'c': pchar++; break; + default: + fprint(2, "Usage: %s [-lwrbc] [file ...]\n", argv0); + exits("usage"); + } ARGEND + if(pline+pword+prune+pbadr+pchar == 0) { + pline = 1; + pword = 1; + pchar = 1; + } + if(argc==0) + count(0, 0); + else{ + for(i=0;i<argc;i++){ + f=open(argv[i], OREAD); + if(f<0){ + perror(argv[i]); + status="can't open"; + } + else{ + count(f, argv[i]); + tnline+=nline; + tnword+=nword; + tnrune+=nrune; + tnbadr+=nbadr; + tnchar+=nchar; + close(f); + } + } + if(argc>1) + report(tnline, tnword, tnrune, tnbadr, tnchar, "total"); + } + exits(status); +} +void +report(uvlong nline, uvlong nword, uvlong nrune, uvlong nbadr, uvlong nchar, char *fname) +{ + char line[1024], word[128]; + line[0] = '\0'; + if(pline){ + sprint(word, " %7llud", nline); + strcat(line, word); + } + if(pword){ + sprint(word, " %7llud", nword); + strcat(line, word); + } + if(prune){ + sprint(word, " %7llud", nrune); + strcat(line, word); + } + if(pbadr){ + sprint(word, " %7llud", nbadr); + strcat(line, word); + } + if(pchar){ + sprint(word, " %7llud", nchar); + strcat(line, word); + } + if(fname){ + sprint(word, " %s", fname); + strcat(line, word); + } + print("%s\n", line+1); +} +/* + * How it works. Start in statesp. Each time we read a character, + * increment various counts, and do state transitions according to the + * following table. If we're not in statesp or statewd when done, the + * file ends with a partial rune. + * | character + * state |09,20| 0a |00-7f|80-bf|c0-df|e0-ef|f0-ff + * -------+-----+-----+-----+-----+-----+-----+----- + * statesp|ASP |ASPN |AWDW |AWDWX|AC2W |AC3W |AWDWX + * statewd|ASP |ASPN |AWD |AWDX |AC2 |AC3 |AWDX + * statec2|ASPX |ASPNX|AWDX |AWDR |AC2X |AC3X |AWDX + * statec3|ASPX |ASPNX|AWDX |AC2R |AC2X |AC3X |AWDX + */ +enum{ /* actions */ + AC2, /* enter statec2 */ + AC2R, /* enter statec2, don't count a rune */ + AC2W, /* enter statec2, count a word */ + AC2X, /* enter statec2, count a bad rune */ + AC3, /* enter statec3 */ + AC3W, /* enter statec3, count a word */ + AC3X, /* enter statec3, count a bad rune */ + ASP, /* enter statesp */ + ASPN, /* enter statesp, count a newline */ + ASPNX, /* enter statesp, count a newline, count a bad rune */ + ASPX, /* enter statesp, count a bad rune */ + AWD, /* enter statewd */ + AWDR, /* enter statewd, don't count a rune */ + AWDW, /* enter statewd, count a word */ + AWDWX, /* enter statewd, count a word, count a bad rune */ + AWDX, /* enter statewd, count a bad rune */ +}; +uchar statesp[256]={ /* looking for the start of a word */ +AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, /* 00-07 */ +AWDW, ASP, ASPN, AWDW, AWDW, AWDW, AWDW, AWDW, /* 08-0f */ +AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, /* 10-17 */ +AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, /* 18-1f */ +ASP, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, /* 20-27 */ +AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, /* 28-2f */ +AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, /* 30-37 */ +AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, /* 38-3f */ +AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, /* 40-47 */ +AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, /* 48-4f */ +AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, /* 50-57 */ +AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, /* 58-5f */ +AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, /* 60-67 */ +AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, /* 68-6f */ +AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, /* 70-77 */ +AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, /* 78-7f */ +AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* 80-87 */ +AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* 88-8f */ +AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* 90-97 */ +AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* 98-9f */ +AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* a0-a7 */ +AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* a8-af */ +AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* b0-b7 */ +AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* b8-bf */ +AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, /* c0-c7 */ +AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, /* c8-cf */ +AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, /* d0-d7 */ +AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, /* d8-df */ +AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, /* e0-e7 */ +AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, /* e8-ef */ +AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* f0-f7 */ +AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* f8-ff */ +}; +uchar statewd[256]={ /* looking for the next character in a word */ +AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, /* 00-07 */ +AWD, ASP, ASPN, AWD, AWD, AWD, AWD, AWD, /* 08-0f */ +AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, /* 10-17 */ +AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, /* 18-1f */ +ASP, AWD, AWD, AWD, AWD, AWD, AWD, AWD, /* 20-27 */ +AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, /* 28-2f */ +AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, /* 30-37 */ +AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, /* 38-3f */ +AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, /* 40-47 */ +AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, /* 48-4f */ +AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, /* 50-57 */ +AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, /* 58-5f */ +AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, /* 60-67 */ +AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, /* 68-6f */ +AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, /* 70-77 */ +AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, /* 78-7f */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 80-87 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 88-8f */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 90-97 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 98-9f */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* a0-a7 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* a8-af */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* b0-b7 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* b8-bf */ +AC2, AC2, AC2, AC2, AC2, AC2, AC2, AC2, /* c0-c7 */ +AC2, AC2, AC2, AC2, AC2, AC2, AC2, AC2, /* c8-cf */ +AC2, AC2, AC2, AC2, AC2, AC2, AC2, AC2, /* d0-d7 */ +AC2, AC2, AC2, AC2, AC2, AC2, AC2, AC2, /* d8-df */ +AC3, AC3, AC3, AC3, AC3, AC3, AC3, AC3, /* e0-e7 */ +AC3, AC3, AC3, AC3, AC3, AC3, AC3, AC3, /* e8-ef */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* f0-f7 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* f8-ff */ +}; +uchar statec2[256]={ /* looking for 10xxxxxx to complete a rune */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 00-07 */ +AWDX, ASPX, ASPNX,AWDX, AWDX, AWDX, AWDX, AWDX, /* 08-0f */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 10-17 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 18-1f */ +ASPX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 20-27 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 28-2f */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 30-37 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 38-3f */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 40-47 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 48-4f */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 50-57 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 58-5f */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 60-67 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 68-6f */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 70-77 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 78-7f */ +AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, /* 80-87 */ +AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, /* 88-8f */ +AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, /* 90-97 */ +AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, /* 98-9f */ +AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, /* a0-a7 */ +AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, /* a8-af */ +AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, /* b0-b7 */ +AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, /* b8-bf */ +AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, /* c0-c7 */ +AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, /* c8-cf */ +AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, /* d0-d7 */ +AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, /* d8-df */ +AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, /* e0-e7 */ +AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, /* e8-ef */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* f0-f7 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* f8-ff */ +}; +uchar statec3[256]={ /* looking for 10xxxxxx,10xxxxxx to complete a rune */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 00-07 */ +AWDX, ASPX, ASPNX,AWDX, AWDX, AWDX, AWDX, AWDX, /* 08-0f */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 10-17 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 18-1f */ +ASPX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 20-27 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 28-2f */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 30-37 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 38-3f */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 40-47 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 48-4f */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 50-57 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 58-5f */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 60-67 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 68-6f */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 70-77 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* 78-7f */ +AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, /* 80-87 */ +AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, /* 88-8f */ +AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, /* 90-97 */ +AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, /* 98-9f */ +AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, /* a0-a7 */ +AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, /* a8-af */ +AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, /* b0-b7 */ +AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, /* b8-bf */ +AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, /* c0-c7 */ +AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, /* c8-cf */ +AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, /* d0-d7 */ +AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, /* d8-df */ +AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, /* e0-e7 */ +AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, /* e8-ef */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* f0-f7 */ +AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, /* f8-ff */ +}; +void +count(int f, char *name) +{ + int n; + uchar buf[NBUF]; + uchar *bufp, *ebuf; + uchar *state=statesp; + + nline = 0; + nword = 0; + nrune = 0; + nbadr = 0; + nchar = 0; + + for(;;){ + n=read(f, buf, NBUF); + if(n<=0) + break; + nchar+=n; + nrune+=n; /* might be too large, gets decreased later */ + bufp=buf; + ebuf=buf+n; + do{ + switch(state[*bufp]){ + case AC2: state=statec2; break; + case AC2R: state=statec2; --nrune; break; + case AC2W: state=statec2; nword++; break; + case AC2X: state=statec2; nbadr++; break; + case AC3: state=statec3; break; + case AC3W: state=statec3; nword++; break; + case AC3X: state=statec3; nbadr++; break; + case ASP: state=statesp; break; + case ASPN: state=statesp; nline++; break; + case ASPNX: state=statesp; nline++; nbadr++; break; + case ASPX: state=statesp; nbadr++; break; + case AWD: state=statewd; break; + case AWDR: state=statewd; --nrune; break; + case AWDW: state=statewd; nword++; break; + case AWDWX: state=statewd; nword++; nbadr++; break; + case AWDX: state=statewd; nbadr++; break; + } + }while(++bufp!=ebuf); + } + if(state!=statesp && state!=statewd) + nbadr++; + if(n<0) + perror(name); + report(nline, nword, nrune, nbadr, nchar, name); +} diff --git a/src/cmd/xd.c b/src/cmd/xd.c new file mode 100644 index 00000000..0d6e1bc3 --- /dev/null +++ b/src/cmd/xd.c @@ -0,0 +1,355 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +unsigned char odata[16]; +unsigned char data[16]; +int ndata; +unsigned long addr; +int repeats; +int swizzle; +int flush; +int abase=2; +int xd(char *, int); +void xprint(char *, long); +void initarg(void), swizz(void); +enum{ + Narg=10 +}; +typedef struct Arg Arg; +typedef void fmtfn(char *); +struct Arg +{ + int ascii; /* 0==none, 1==ascii */ + int loglen; /* 0==1, 1==2, 2==4, 3==8 */ + int base; /* 0==8, 1==10, 2==16 */ + fmtfn *fn; /* function to call with data */ + char *afmt; /* format to use to print address */ + char *fmt; /* format to use to print data */ +}arg[Narg]; +int narg; + +fmtfn fmt0, fmt1, fmt2, fmt3, fmtc; +fmtfn *fmt[4] = { + fmt0, + fmt1, + fmt2, + fmt3 +}; + +char *dfmt[4][3] = { + " %.3uo", " %.3ud", " %.2ux", + " %.6uo", " %.5ud", " %.4ux", + " %.11luo", " %.10lud", " %.8lux", + " %.22lluo", " %.20llud", " %.16llux", +}; + +char *cfmt[3][3] = { + " %c", " %c", " %c", + " %.3s", " %.3s", " %.2s", + " %.3uo", " %.3ud", " %.2ux", +}; + +char *afmt[2][3] = { + "%.7luo ", "%.7lud ", "%.7lux ", + "%7luo ", "%7lud ", "%7lux ", +}; + +Biobuf bin; +Biobuf bout; + +void +main(int argc, char *argv[]) +{ + int i, err; + Arg *ap; + + Binit(&bout, 1, OWRITE); + err = 0; + ap = 0; + while(argc>1 && argv[1][0]=='-' && argv[1][1]){ + --argc; + argv++; + argv[0]++; + if(argv[0][0] == 'r'){ + repeats = 1; + if(argv[0][1]) + goto Usage; + continue; + } + if(argv[0][0] == 's'){ + swizzle = 1; + if(argv[0][1]) + goto Usage; + continue; + } + if(argv[0][0] == 'u'){ + flush = 1; + if(argv[0][1]) + goto Usage; + continue; + } + if(argv[0][0] == 'a'){ + argv[0]++; + switch(argv[0][0]){ + case 'o': + abase = 0; + break; + case 'd': + abase = 1; + break; + case 'x': + abase = 2; + break; + default: + goto Usage; + } + if(argv[0][1]) + goto Usage; + continue; + } + ap = &arg[narg]; + initarg(); + while(argv[0][0]){ + switch(argv[0][0]){ + case 'c': + ap->ascii = 1; + ap->loglen = 0; + if(argv[0][1] || argv[0][-1]!='-') + goto Usage; + break; + case 'o': + ap->base = 0; + break; + case 'd': + ap->base = 1; + break; + case 'x': + ap->base = 2; + break; + case 'b': + case '1': + ap->loglen = 0; + break; + case 'w': + case '2': + ap->loglen = 1; + break; + case 'l': + case '4': + ap->loglen = 2; + break; + case 'v': + case '8': + ap->loglen = 3; + break; + default: + Usage: + fprint(2, "usage: xd [-u] [-r] [-s] [-a{odx}] [-c|{b1w2l4v8}{odx}] ... file ...\n"); + exits("usage"); + } + argv[0]++; + } + if(ap->ascii) + ap->fn = fmtc; + else + ap->fn = fmt[ap->loglen]; + ap->fmt = dfmt[ap->loglen][ap->base]; + ap->afmt = afmt[ap>arg][abase]; + } + if(narg == 0) + initarg(); + if(argc == 1) + err = xd(0, 0); + else if(argc == 2) + err = xd(argv[1], 0); + else for(i=1; i<argc; i++) + err |= xd(argv[i], 1); + exits(err? "error" : 0); +} + +void +initarg(void) +{ + Arg *ap; + + ap = &arg[narg++]; + if(narg >= Narg){ + fprint(2, "xd: too many formats (max %d)\n", Narg); + exits("usage"); + } + ap->ascii = 0; + ap->loglen = 2; + ap->base = 2; + ap->fn = fmt2; + ap->fmt = dfmt[ap->loglen][ap->base]; + ap->afmt = afmt[narg>1][abase]; +} + +int +xd(char *name, int title) +{ + int fd; + int i, star; + Arg *ap; + Biobuf *bp; + + fd = 0; + if(name){ + bp = Bopen(name, OREAD); + if(bp == 0){ + fprint(2, "xd: can't open %s\n", name); + return 1; + } + }else{ + bp = &bin; + Binit(bp, fd, OREAD); + } + if(title) + xprint("%s\n", (long)name); + addr = 0; + star = 0; + while((ndata=Bread(bp, data, 16)) >= 0){ + if(ndata < 16) + for(i=ndata; i<16; i++) + data[i] = 0; + if(swizzle) + swizz(); + if(ndata==16 && repeats){ + if(addr>0 && data[0]==odata[0]){ + for(i=1; i<16; i++) + if(data[i] != odata[i]) + break; + if(i == 16){ + addr += 16; + if(star == 0){ + star++; + xprint("*\n", 0); + } + continue; + } + } + for(i=0; i<16; i++) + odata[i] = data[i]; + star = 0; + } + for(ap=arg; ap<&arg[narg]; ap++){ + xprint(ap->afmt, addr); + (*ap->fn)(ap->fmt); + xprint("\n", 0); + if(flush) + Bflush(&bout); + } + addr += ndata; + if(ndata<16){ + xprint(afmt[0][abase], addr); + xprint("\n", 0); + if(flush) + Bflush(&bout); + break; + } + } + Bterm(bp); + return 0; +} + +void +swizz(void) +{ + uchar *p, *q; + int i; + uchar swdata[16]; + + p = data; + q = swdata; + for(i=0; i<16; i++) + *q++ = *p++; + p = data; + q = swdata; + for(i=0; i<4; i++){ + p[0] = q[3]; + p[1] = q[2]; + p[2] = q[1]; + p[3] = q[0]; + p += 4; + q += 4; + } +} + +void +fmt0(char *f) +{ + int i; + for(i=0; i<ndata; i++) + xprint(f, data[i]); +} + +void +fmt1(char *f) +{ + int i; + for(i=0; i<ndata; i+=sizeof(unsigned short)) + xprint(f, (data[i]<<8)|data[i+1]); +} + +void +fmt2(char *f) +{ + int i; + for(i=0; i<ndata; i+=sizeof(unsigned long)) + xprint(f, (data[i]<<24)|(data[i+1]<<16)|(data[i+2]<<8)|data[i+3]); +} + +void +fmt3(char *f) +{ + int i; + unsigned long long v; + for(i=0; i<ndata; i+=sizeof(unsigned long long)){ + v = (data[i]<<24)|(data[i+1]<<16)|(data[i+2]<<8)|data[i+3]; + v <<= 32; + v |= (data[i+4]<<24)|(data[i+1+4]<<16)|(data[i+2+4]<<8)|data[i+3+4]; + if(Bprint(&bout, f, v)<0){ + fprint(2, "xd: i/o error\n"); + exits("i/o error"); + } + } +} + +void +fmtc(char *f) +{ + int i; + + USED(f); + for(i=0; i<ndata; i++) + switch(data[i]){ + case '\t': + xprint(cfmt[1][2], (long)"\\t"); + break; + case '\r': + xprint(cfmt[1][2], (long)"\\r"); + break; + case '\n': + xprint(cfmt[1][2], (long)"\\n"); + break; + case '\b': + xprint(cfmt[1][2], (long)"\\b"); + break; + default: + if(data[i]>=0x7F || ' '>data[i]) + xprint(cfmt[2][2], data[i]); + else + xprint(cfmt[0][2], data[i]); + break; + } +} + +void +xprint(char *fmt, long d) +{ + if(Bprint(&bout, fmt, d)<0){ + fprint(2, "xd: i/o error\n"); + exits("i/o error"); + } +} diff --git a/src/cmd/yacc.c b/src/cmd/yacc.c new file mode 100644 index 00000000..a9a2b2d7 --- /dev/null +++ b/src/cmd/yacc.c @@ -0,0 +1,2939 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <ctype.h> + +#define Bungetrune Bungetc /* ok for now. */ + +/* + * all these are 32 bit + */ +#define TBITSET ((32+NTERMS)/32) /* BOTCH?? +31 */ +#define BIT(a,i) ((a)[(i)>>5] & (1<<((i)&037))) +#define SETBIT(a,i) ((a)[(i)>>5] |= (1<<((i)&037))) +#define NWORDS(n) (((n)+32)/32) + +#define PARSER "/sys/lib/yaccpar" +#define PARSERS "/sys/lib/yaccpars" +#define TEMPNAME "y.tmp.XXXXXX" +#define ACTNAME "y.acts.XXXXXX" +#define OFILE "tab.c" +#define FILEU "output" +#define FILED "tab.h" +#define FILEDEBUG "debug" + +enum +{ +/* + * the following are adjustable + * according to memory size + */ + ACTSIZE = 40000, + MEMSIZE = 40000, + NSTATES = 2000, + NTERMS = 511, + NPROD = 1600, + NNONTERM = 600, + TEMPSIZE = 2000, + CNAMSZ = 10000, + LSETSIZE = 2400, + WSETSIZE = 350, + + NAMESIZE = 50, + NTYPES = 63, + ISIZE = 400, + + PRIVATE = 0xE000, /* unicode private use */ + + /* relationships which must hold: + TBITSET ints must hold NTERMS+1 bits... + WSETSIZE >= NNONTERM + LSETSIZE >= NNONTERM + TEMPSIZE >= NTERMS + NNONTERM + 1 + TEMPSIZE >= NSTATES + */ + + NTBASE = 010000, + ERRCODE = 8190, + ACCEPTCODE = 8191, + + NOASC = 0, /* no assoc. */ + LASC = 1, /* left assoc. */ + RASC = 2, /* right assoc. */ + BASC = 3, /* binary assoc. */ + + /* flags for state generation */ + + DONE = 0, + MUSTDO = 1, + MUSTLOOKAHEAD = 2, + + /* flags for a rule having an action, and being reduced */ + + ACTFLAG = 04, + REDFLAG = 010, + + /* output parser flags */ + YYFLAG1 = -1000, + + /* parse tokens */ + IDENTIFIER = PRIVATE, + MARK, + TERM, + LEFT, + RIGHT, + BINARY, + PREC, + LCURLY, + IDENTCOLON, + NUMBER, + START, + TYPEDEF, + TYPENAME, + UNION, + + ENDFILE = 0, + + EMPTY = 1, + WHOKNOWS = 0, + OK = 1, + NOMORE = -1000, +}; + + /* macros for getting associativity and precedence levels */ + +#define ASSOC(i) ((i)&03) +#define PLEVEL(i) (((i)>>4)&077) +#define TYPE(i) (((i)>>10)&077) + + /* macros for setting associativity and precedence levels */ + +#define SETASC(i,j) i |= j +#define SETPLEV(i,j) i |= (j<<4) +#define SETTYPE(i,j) i |= (j<<10) + + /* looping macros */ + +#define TLOOP(i) for(i=1; i<=ntokens; i++) +#define NTLOOP(i) for(i=0; i<=nnonter; i++) +#define PLOOP(s,i) for(i=s; i<nprod; i++) +#define SLOOP(i) for(i=0; i<nstate; i++) +#define WSBUMP(x) x++ +#define WSLOOP(s,j) for(j=s; j<cwp; j++) +#define ITMLOOP(i,p,q) for(q=pstate[i+1], p=pstate[i]; p<q; p++) +#define SETLOOP(i) for(i=0; i<tbitset; i++) + + /* command to clobber tempfiles after use */ + +#define ZAPFILE(x) if(x) remove(x) + + /* I/O descriptors */ + +Biobuf* faction; /* file for saving actions */ +Biobuf* fdefine; /* file for #defines */ +Biobuf* fdebug; /* y.debug for strings for debugging */ +Biobuf* ftable; /* y.tab.c file */ +Biobuf* ftemp; /* tempfile to pass 2 */ +Biobuf* finput; /* input file */ +Biobuf* foutput; /* y.output file */ + + /* communication variables between various I/O routines */ + +char* infile; /* input file name */ +int numbval; /* value of an input number */ +char tokname[NAMESIZE+4]; /* input token name, slop for runes and 0 */ + + /* structure declarations */ + +typedef +struct +{ + int lset[TBITSET]; +} Lkset; + +typedef +struct +{ + int* pitem; + Lkset* look; +} Item; + +typedef +struct +{ + char* name; + int value; +} Symb; + +typedef +struct +{ + int* pitem; + int flag; + Lkset ws; +} Wset; + + /* storage of names */ + +char cnames[CNAMSZ]; /* place where token and nonterminal names are stored */ +int cnamsz = CNAMSZ; /* size of cnames */ +char* cnamp = cnames; /* place where next name is to be put in */ +int ndefout = 4; /* number of defined symbols output */ +char* tempname; +char* actname; +char ttempname[] = TEMPNAME; +char tactname[] = ACTNAME; +char* parser = PARSER; +char* yydebug; + + /* storage of types */ +int ntypes; /* number of types defined */ +char* typeset[NTYPES]; /* pointers to type tags */ + + /* token information */ + +int ntokens = 0 ; /* number of tokens */ +Symb tokset[NTERMS]; +int toklev[NTERMS]; /* vector with the precedence of the terminals */ + + /* nonterminal information */ + +int nnonter = -1; /* the number of nonterminals */ +Symb nontrst[NNONTERM]; +int start; /* start symbol */ + + /* assigned token type values */ +int extval = 0; + +char* ytabc = OFILE; /* name of y.tab.c */ + + /* grammar rule information */ + +int mem0[MEMSIZE] ; /* production storage */ +int* mem = mem0; +int nprod = 1; /* number of productions */ +int* prdptr[NPROD]; /* pointers to descriptions of productions */ +int levprd[NPROD]; /* precedence levels for the productions */ +int rlines[NPROD]; /* line number for this rule */ + + /* state information */ + +int nstate = 0; /* number of states */ +Item* pstate[NSTATES+2]; /* pointers to the descriptions of the states */ +int tystate[NSTATES]; /* contains type information about the states */ +int defact[NSTATES]; /* the default actions of states */ +int tstates[NTERMS]; /* states generated by terminal gotos */ +int ntstates[NNONTERM]; /* states generated by nonterminal gotos */ +int mstates[NSTATES]; /* chain of overflows of term/nonterm generation lists */ +int lastred; /* the number of the last reduction of a state */ + + /* lookahead set information */ + +Lkset lkst[LSETSIZE]; +int nolook; /* flag to turn off lookahead computations */ +int tbitset; /* size of lookahead sets */ +int nlset = 0; /* next lookahead set index */ +int nolook = 0; /* flag to suppress lookahead computations */ +Lkset clset; /* temporary storage for lookahead computations */ + + /* working set information */ + +Wset wsets[WSETSIZE]; +Wset* cwp; + + /* storage for action table */ + +int amem[ACTSIZE]; /* action table storage */ +int* memp = amem; /* next free action table position */ +int indgo[NSTATES]; /* index to the stored goto table */ + + /* temporary vector, indexable by states, terms, or ntokens */ + +int temp1[TEMPSIZE]; /* temporary storage, indexed by terms + ntokens or states */ +int lineno = 1; /* current input line number */ +int fatfl = 1; /* if on, error is fatal */ +int nerrors = 0; /* number of errors */ + + /* statistics collection variables */ + +int zzgoent; +int zzgobest; +int zzacent; +int zzexcp; +int zzclose; +int zzrrconf; +int zzsrconf; + +int* ggreed = lkst[0].lset; +int* pgo = wsets[0].ws.lset; +int* yypgo = &nontrst[0].value; + +int maxspr = 0; /* maximum spread of any entry */ +int maxoff = 0; /* maximum offset into a array */ +int* pmem = mem0; +int* maxa; +int nxdb = 0; +int adb = 0; + + + /* storage for information about the nonterminals */ + +int** pres[NNONTERM+2]; /* vector of pointers to productions yielding each nonterminal */ +Lkset* pfirst[NNONTERM+2]; /* vector of pointers to first sets for each nonterminal */ +int pempty[NNONTERM+1]; /* vector of nonterminals nontrivially deriving e */ + + /* random stuff picked out from between functions */ + +int indebug = 0; +Wset* zzcwp = wsets; +int zzgoent = 0; +int zzgobest = 0; +int zzacent = 0; +int zzexcp = 0; +int zzclose = 0; +int zzsrconf = 0; +int* zzmemsz = mem0; +int zzrrconf = 0; +int pidebug = 0; /* debugging flag for putitem */ +int gsdebug = 0; +int cldebug = 0; /* debugging flag for closure */ +int pkdebug = 0; +int g2debug = 0; + +struct +{ + char* name; + long value; +} resrv[] = +{ + "binary", BINARY, + "left", LEFT, + "nonassoc", BINARY, + "prec", PREC, + "right", RIGHT, + "start", START, + "term", TERM, + "token", TERM, + "type", TYPEDEF, + "union", UNION, + 0, +}; + + /* define functions */ + +void main(int, char**); +void others(void); +char* chcopy(char*, char*); +char* writem(int*); +char* symnam(int); +void summary(void); +void error(char*, ...); +void aryfil(int*, int, int); +int setunion(int*, int*); +void prlook(Lkset*); +void cpres(void); +void cpfir(void); +int state(int); +void putitem(int*, Lkset*); +void cempty(void); +void stagen(void); +void closure(int); +Lkset* flset(Lkset*); +void cleantmp(void); +void intr(void); +void setup(int, char**); +void finact(void); +int defin(int, char*); +void defout(int); +char* cstash(char*); +long gettok(void); +int fdtype(int); +int chfind(int, char*); +void cpyunion(void); +void cpycode(void); +int skipcom(void); +void cpyact(int); +void openup(char*, int, int, int, char*); +void output(void); +int apack(int*, int); +void go2out(void); +void go2gen(int); +void precftn(int, int, int); +void wract(int); +void wrstate(int); +void warray(char*, int*, int); +void hideprod(void); +void callopt(void); +void gin(int); +void stin(int); +int nxti(void); +void osummary(void); +void aoutput(void); +void arout(char*, int*, int); +int gtnm(void); + +void +main(int argc, char *argv[]) +{ + + setup(argc, argv); /* initialize and read productions */ + tbitset = NWORDS(ntokens); + cpres(); /* make table of which productions yield a given nonterminal */ + cempty(); /* make a table of which nonterminals can match the empty string */ + cpfir(); /* make a table of firsts of nonterminals */ + stagen(); /* generate the states */ + output(); /* write the states and the tables */ + go2out(); + hideprod(); + summary(); + callopt(); + others(); + exits(0); +} + +/* + * put out other arrays, copy the parsers + */ +void +others(void) +{ + int c, i, j; + + finput = Bopen(parser, OREAD); + if(finput == 0) + error("cannot find parser %s", parser); + warray("yyr1", levprd, nprod); + aryfil(temp1, nprod, 0); + PLOOP(1, i) + temp1[i] = prdptr[i+1]-prdptr[i]-2; + warray("yyr2", temp1, nprod); + + aryfil(temp1, nstate, -1000); + TLOOP(i) + for(j=tstates[i]; j!=0; j=mstates[j]) + temp1[j] = i; + NTLOOP(i) + for(j=ntstates[i]; j!=0; j=mstates[j]) + temp1[j] = -i; + warray("yychk", temp1, nstate); + warray("yydef", defact, nstate); + + /* put out token translation tables */ + /* table 1 has 0-256 */ + aryfil(temp1, 256, 0); + c = 0; + TLOOP(i) { + j = tokset[i].value; + if(j >= 0 && j < 256) { + if(temp1[j]) { + print("yacc bug -- cant have 2 different Ts with same value\n"); + print(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name); + nerrors++; + } + temp1[j] = i; + if(j > c) + c = j; + } + } + warray("yytok1", temp1, c+1); + + /* table 2 has PRIVATE-PRIVATE+256 */ + aryfil(temp1, 256, 0); + c = 0; + TLOOP(i) { + j = tokset[i].value - PRIVATE; + if(j >= 0 && j < 256) { + if(temp1[j]) { + print("yacc bug -- cant have 2 different Ts with same value\n"); + print(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name); + nerrors++; + } + temp1[j] = i; + if(j > c) + c = j; + } + } + warray("yytok2", temp1, c+1); + + /* table 3 has everything else */ + Bprint(ftable, "long yytok3[] =\n{\n"); + c = 0; + TLOOP(i) { + j = tokset[i].value; + if(j >= 0 && j < 256) + continue; + if(j >= PRIVATE && j < 256+PRIVATE) + continue; + + Bprint(ftable, "%4d,%4d,", j, i); + c++; + if(c%5 == 0) + Bprint(ftable, "\n"); + } + Bprint(ftable, "%4d\n};\n", 0); + + /* copy parser text */ + while((c=Bgetrune(finput)) != Beof) { + if(c == '$') { + if((c = Bgetrune(finput)) != 'A') + Bputrune(ftable, '$'); + else { /* copy actions */ + faction = Bopen(actname, OREAD); + if(faction == 0) + error("cannot reopen action tempfile"); + while((c=Bgetrune(faction)) != Beof) + Bputrune(ftable, c); + Bterm(faction); + ZAPFILE(actname); + c = Bgetrune(finput); + } + } + Bputrune(ftable, c); + } + Bterm(ftable); +} + +/* + * copies string q into p, returning next free char ptr + */ +char* +chcopy(char* p, char* q) +{ + int c; + + while(c = *q) { + if(c == '"') + *p++ = '\\'; + *p++ = c; + q++; + } + *p = 0; + return p; +} + +/* + * creates output string for item pointed to by pp + */ +char* +writem(int *pp) +{ + int i,*p; + static char sarr[ISIZE]; + char* q; + + for(p=pp; *p>0; p++) + ; + p = prdptr[-*p]; + q = chcopy(sarr, nontrst[*p-NTBASE].name); + q = chcopy(q, ": "); + for(;;) { + *q = ' '; + p++; + if(p == pp) + *q = '.'; + q++; + *q = '\0'; + i = *p; + if(i <= 0) + break; + q = chcopy(q, symnam(i)); + if(q > &sarr[ISIZE-30]) + error("item too big"); + } + + /* an item calling for a reduction */ + i = *pp; + if(i < 0 ) { + q = chcopy(q, " ("); + sprint(q, "%d)", -i); + } + return sarr; +} + +/* + * return a pointer to the name of symbol i + */ +char* +symnam(int i) +{ + char* cp; + + cp = (i >= NTBASE)? nontrst[i-NTBASE].name: tokset[i].name; + if(*cp == ' ') + cp++; + return cp; +} + +/* + * output the summary on y.output + */ +void +summary(void) +{ + + if(foutput != 0) { + Bprint(foutput, "\n%d/%d terminals, %d/%d nonterminals\n", + ntokens, NTERMS, nnonter, NNONTERM); + Bprint(foutput, "%d/%d grammar rules, %d/%d states\n", + nprod, NPROD, nstate, NSTATES); + Bprint(foutput, "%d shift/reduce, %d reduce/reduce conflicts reported\n", + zzsrconf, zzrrconf); + Bprint(foutput, "%d/%d working sets used\n", + (int)(zzcwp-wsets), WSETSIZE); + Bprint(foutput, "memory: states,etc. %d/%d, parser %d/%d\n", + (int)(zzmemsz-mem0), MEMSIZE, (int)(memp-amem), ACTSIZE); + Bprint(foutput, "%d/%d distinct lookahead sets\n", nlset, LSETSIZE); + Bprint(foutput, "%d extra closures\n", zzclose - 2*nstate); + Bprint(foutput, "%d shift entries, %d exceptions\n", zzacent, zzexcp); + Bprint(foutput, "%d goto entries\n", zzgoent); + Bprint(foutput, "%d entries saved by goto default\n", zzgobest); + } + if(zzsrconf != 0 || zzrrconf != 0) { + print("\nconflicts: "); + if(zzsrconf) + print("%d shift/reduce", zzsrconf); + if(zzsrconf && zzrrconf) + print(", "); + if(zzrrconf) + print("%d reduce/reduce", zzrrconf); + print("\n"); + } + if(ftemp != 0) { + Bterm(ftemp); + ftemp = 0; + } + if(fdefine != 0) { + Bterm(fdefine); + fdefine = 0; + } +} + +/* + * write out error comment -- NEEDS WORK + */ +void +error(char *s, ...) +{ + + nerrors++; + fprint(2, "\n fatal error:"); + fprint(2, s, (&s)[1]); + fprint(2, ", %s:%d\n", infile, lineno); + if(!fatfl) + return; + summary(); + cleantmp(); + exits("error"); +} + +/* + * set elements 0 through n-1 to c + */ +void +aryfil(int *v, int n, int c) +{ + int i; + + for(i=0; i<n; i++) + v[i] = c; +} + +/* + * set a to the union of a and b + * return 1 if b is not a subset of a, 0 otherwise + */ +int +setunion(int *a, int *b) +{ + int i, x, sub; + + sub = 0; + SETLOOP(i) { + x = *a; + *a |= *b; + if(*a != x) + sub = 1; + a++; + b++; + } + return sub; +} + +void +prlook(Lkset* p) +{ + int j, *pp; + + pp = p->lset; + if(pp == 0) + Bprint(foutput, "\tNULL"); + else { + Bprint(foutput, " { "); + TLOOP(j) + if(BIT(pp,j)) + Bprint(foutput, "%s ", symnam(j)); + Bprint(foutput, "}"); + } +} + +/* + * compute an array with the beginnings of productions yielding given nonterminals + * The array pres points to these lists + * the array pyield has the lists: the total size is only NPROD+1 + */ +void +cpres(void) +{ + int c, j, i, **pmem; + static int *pyield[NPROD]; + + pmem = pyield; + NTLOOP(i) { + c = i+NTBASE; + pres[i] = pmem; + fatfl = 0; /* make undefined symbols nonfatal */ + PLOOP(0, j) + if(*prdptr[j] == c) + *pmem++ = prdptr[j]+1; + if(pres[i] == pmem) + error("nonterminal %s not defined!", nontrst[i].name); + } + pres[i] = pmem; + fatfl = 1; + if(nerrors) { + summary(); + cleantmp(); + exits("error"); + } + if(pmem != &pyield[nprod]) + error("internal Yacc error: pyield %d", pmem-&pyield[nprod]); +} + +/* + * compute an array with the first of nonterminals + */ +void +cpfir(void) +{ + int *p, **s, i, **t, ch, changes; + + zzcwp = &wsets[nnonter]; + NTLOOP(i) { + aryfil(wsets[i].ws.lset, tbitset, 0); + t = pres[i+1]; + /* initially fill the sets */ + for(s=pres[i]; s<t; ++s) + for(p = *s; (ch = *p) > 0; ++p) { + if(ch < NTBASE) { + SETBIT(wsets[i].ws.lset, ch); + break; + } + if(!pempty[ch-NTBASE]) + break; + } + } + + /* now, reflect transitivity */ + changes = 1; + while(changes) { + changes = 0; + NTLOOP(i) { + t = pres[i+1]; + for(s = pres[i]; s < t; ++s) + for(p = *s; (ch = (*p-NTBASE)) >= 0; ++p) { + changes |= setunion(wsets[i].ws.lset, wsets[ch].ws.lset); + if(!pempty[ch]) + break; + } + } + } + + NTLOOP(i) + pfirst[i] = flset(&wsets[i].ws); + if(!indebug) + return; + if(foutput != 0) + NTLOOP(i) { + Bprint(foutput, "\n%s: ", nontrst[i].name); + prlook(pfirst[i]); + Bprint(foutput, " %d\n", pempty[i]); + } +} + +/* + * sorts last state,and sees if it equals earlier ones. returns state number + */ +int +state(int c) +{ + Item *p1, *p2, *k, *l, *q1, *q2; + int size1, size2, i; + + p1 = pstate[nstate]; + p2 = pstate[nstate+1]; + if(p1 == p2) + return 0; /* null state */ + /* sort the items */ + for(k = p2-1; k > p1; k--) /* make k the biggest */ + for(l = k-1; l >= p1; --l) + if(l->pitem > k->pitem) { + int *s; + Lkset *ss; + + s = k->pitem; + k->pitem = l->pitem; + l->pitem = s; + ss = k->look; + k->look = l->look; + l->look = ss; + } + size1 = p2 - p1; /* size of state */ + + for(i = (c>=NTBASE)? ntstates[c-NTBASE]: tstates[c]; i != 0; i = mstates[i]) { + /* get ith state */ + q1 = pstate[i]; + q2 = pstate[i+1]; + size2 = q2 - q1; + if(size1 != size2) + continue; + k = p1; + for(l = q1; l < q2; l++) { + if(l->pitem != k->pitem) + break; + k++; + } + if(l != q2) + continue; + /* found it */ + pstate[nstate+1] = pstate[nstate]; /* delete last state */ + /* fix up lookaheads */ + if(nolook) + return i; + for(l = q1, k = p1; l < q2; ++l, ++k ) { + int s; + + SETLOOP(s) + clset.lset[s] = l->look->lset[s]; + if(setunion(clset.lset, k->look->lset)) { + tystate[i] = MUSTDO; + /* register the new set */ + l->look = flset( &clset ); + } + } + return i; + } + /* state is new */ + if(nolook) + error("yacc state/nolook error"); + pstate[nstate+2] = p2; + if(nstate+1 >= NSTATES) + error("too many states"); + if(c >= NTBASE) { + mstates[nstate] = ntstates[c-NTBASE]; + ntstates[c-NTBASE] = nstate; + } else { + mstates[nstate] = tstates[c]; + tstates[c] = nstate; + } + tystate[nstate] = MUSTDO; + return nstate++; +} + +void +putitem(int *ptr, Lkset *lptr) +{ + Item *j; + + if(pidebug && foutput != 0) + Bprint(foutput, "putitem(%s), state %d\n", writem(ptr), nstate); + j = pstate[nstate+1]; + j->pitem = ptr; + if(!nolook) + j->look = flset(lptr); + pstate[nstate+1] = ++j; + if((int*)j > zzmemsz) { + zzmemsz = (int*)j; + if(zzmemsz >= &mem0[MEMSIZE]) + error("out of state space"); + } +} + +/* + * mark nonterminals which derive the empty string + * also, look for nonterminals which don't derive any token strings + */ +void +cempty(void) +{ + + int i, *p; + + /* first, use the array pempty to detect productions that can never be reduced */ + /* set pempty to WHONOWS */ + aryfil(pempty, nnonter+1, WHOKNOWS); + + /* now, look at productions, marking nonterminals which derive something */ +more: + PLOOP(0, i) { + if(pempty[*prdptr[i] - NTBASE]) + continue; + for(p = prdptr[i]+1; *p >= 0; ++p) + if(*p >= NTBASE && pempty[*p-NTBASE] == WHOKNOWS) + break; + /* production can be derived */ + if(*p < 0) { + pempty[*prdptr[i]-NTBASE] = OK; + goto more; + } + } + + /* now, look at the nonterminals, to see if they are all OK */ + NTLOOP(i) { + /* the added production rises or falls as the start symbol ... */ + if(i == 0) + continue; + if(pempty[i] != OK) { + fatfl = 0; + error("nonterminal %s never derives any token string", nontrst[i].name); + } + } + + if(nerrors) { + summary(); + cleantmp(); + exits("error"); + } + + /* now, compute the pempty array, to see which nonterminals derive the empty string */ + /* set pempty to WHOKNOWS */ + aryfil( pempty, nnonter+1, WHOKNOWS); + + /* loop as long as we keep finding empty nonterminals */ + +again: + PLOOP(1, i) { + /* not known to be empty */ + if(pempty[*prdptr[i]-NTBASE] == WHOKNOWS) { + for(p = prdptr[i]+1; *p >= NTBASE && pempty[*p-NTBASE] == EMPTY ; ++p) + ; + /* we have a nontrivially empty nonterminal */ + if(*p < 0) { + pempty[*prdptr[i]-NTBASE] = EMPTY; + /* got one ... try for another */ + goto again; + } + } + } +} + +/* + * generate the states + */ +void +stagen(void) +{ + + int c, i, j, more; + Wset *p, *q; + + /* initialize */ + nstate = 0; + + /* THIS IS FUNNY from the standpoint of portability + * it represents the magic moment when the mem0 array, which has + * been holding the productions, starts to hold item pointers, of a + * different type... + * someday, alloc should be used to allocate all this stuff... for now, we + * accept that if pointers don't fit in integers, there is a problem... + */ + + pstate[0] = pstate[1] = (Item*)mem; + aryfil(clset.lset, tbitset, 0); + putitem(prdptr[0]+1, &clset); + tystate[0] = MUSTDO; + nstate = 1; + pstate[2] = pstate[1]; + + aryfil(amem, ACTSIZE, 0); + + /* now, the main state generation loop */ + for(more=1; more;) { + more = 0; + SLOOP(i) { + if(tystate[i] != MUSTDO) + continue; + tystate[i] = DONE; + aryfil(temp1, nnonter+1, 0); + /* take state i, close it, and do gotos */ + closure(i); + /* generate goto's */ + WSLOOP(wsets, p) { + if(p->flag) + continue; + p->flag = 1; + c = *(p->pitem); + if(c <= 1) { + if(pstate[i+1]-pstate[i] <= p-wsets) + tystate[i] = MUSTLOOKAHEAD; + continue; + } + /* do a goto on c */ + WSLOOP(p, q) + /* this item contributes to the goto */ + if(c == *(q->pitem)) { + putitem(q->pitem+1, &q->ws); + q->flag = 1; + } + if(c < NTBASE) + state(c); /* register new state */ + else + temp1[c-NTBASE] = state(c); + } + if(gsdebug && foutput != 0) { + Bprint(foutput, "%d: ", i); + NTLOOP(j) + if(temp1[j]) + Bprint(foutput, "%s %d, ", + nontrst[j].name, temp1[j]); + Bprint(foutput, "\n"); + } + indgo[i] = apack(&temp1[1], nnonter-1) - 1; + /* do some more */ + more = 1; + } + } +} + +/* + * generate the closure of state i + */ +void +closure(int i) +{ + + Wset *u, *v; + Item *p, *q; + int c, ch, work, k, *pi, **s, **t; + + zzclose++; + + /* first, copy kernel of state i to wsets */ + cwp = wsets; + ITMLOOP(i, p, q) { + cwp->pitem = p->pitem; + cwp->flag = 1; /* this item must get closed */ + SETLOOP(k) + cwp->ws.lset[k] = p->look->lset[k]; + WSBUMP(cwp); + } + + /* now, go through the loop, closing each item */ + work = 1; + while(work) { + work = 0; + WSLOOP(wsets, u) { + if(u->flag == 0) + continue; + /* dot is before c */ + c = *(u->pitem); + if(c < NTBASE) { + u->flag = 0; + /* only interesting case is where . is before nonterminal */ + continue; + } + + /* compute the lookahead */ + aryfil(clset.lset, tbitset, 0); + + /* find items involving c */ + WSLOOP(u, v) + if(v->flag == 1 && *(pi=v->pitem) == c) { + v->flag = 0; + if(nolook) + continue; + while((ch = *++pi) > 0) { + /* terminal symbol */ + if(ch < NTBASE) { + SETBIT(clset.lset, ch); + break; + } + /* nonterminal symbol */ + setunion(clset.lset, pfirst[ch-NTBASE]->lset); + if(!pempty[ch-NTBASE]) + break; + } + if(ch <= 0) + setunion(clset.lset, v->ws.lset); + } + + /* + * now loop over productions derived from c + * c is now nonterminal number + */ + c -= NTBASE; + t = pres[c+1]; + for(s = pres[c]; s < t; ++s) { + /* + * put these items into the closure + * is the item there + */ + WSLOOP(wsets, v) + /* yes, it is there */ + if(v->pitem == *s) { + if(nolook) + goto nexts; + if(setunion(v->ws.lset, clset.lset)) + v->flag = work = 1; + goto nexts; + } + + /* not there; make a new entry */ + if(cwp-wsets+1 >= WSETSIZE) + error( "working set overflow"); + cwp->pitem = *s; + cwp->flag = 1; + if(!nolook) { + work = 1; + SETLOOP(k) cwp->ws.lset[k] = clset.lset[k]; + } + WSBUMP(cwp); + + nexts:; + } + } + } + + /* have computed closure; flags are reset; return */ + if(cwp > zzcwp) + zzcwp = cwp; + if(cldebug && foutput != 0) { + Bprint(foutput, "\nState %d, nolook = %d\n", i, nolook); + WSLOOP(wsets, u) { + if(u->flag) + Bprint(foutput, "flag set!\n"); + u->flag = 0; + Bprint(foutput, "\t%s", writem(u->pitem)); + prlook(&u->ws); + Bprint(foutput, "\n"); + } + } +} + +/* + * decide if the lookahead set pointed to by p is known + * return pointer to a perminent location for the set + */ +Lkset* +flset(Lkset *p) +{ + Lkset *q; + int *u, *v, *w, j; + + for(q = &lkst[nlset]; q-- > lkst;) { + u = p->lset; + v = q->lset; + w = &v[tbitset]; + while(v < w) + if(*u++ != *v++) + goto more; + /* we have matched */ + return q; + more:; + } + /* add a new one */ + q = &lkst[nlset++]; + if(nlset >= LSETSIZE) + error("too many lookahead sets"); + SETLOOP(j) + q->lset[j] = p->lset[j]; + return q; +} + +void +cleantmp(void) +{ + ZAPFILE(actname); + ZAPFILE(tempname); +} + +void +intr(void) +{ + cleantmp(); + exits("interrupted"); +} + +void +setup(int argc, char *argv[]) +{ + long c, t; + int i, j, fd, lev, ty, ytab, *p; + int vflag, dflag, stem; + char actnm[8], *stemc, *s, dirbuf[128]; + + ytab = 0; + vflag = 0; + dflag = 0; + stem = 0; + stemc = "y"; + foutput = 0; + fdefine = 0; + fdebug = 0; + ARGBEGIN{ + case 'v': + case 'V': + vflag++; + break; + case 'D': + yydebug = ARGF(); + break; + case 'd': + dflag++; + break; + case 'o': + ytab++; + ytabc = ARGF(); + break; + case 's': + stem++; + stemc = ARGF(); + break; + case 'S': + parser = PARSERS; + break; + default: + error("illegal option: %c", ARGC()); + }ARGEND + openup(stemc, dflag, vflag, ytab, ytabc); + + if((fd = mkstemp(ttempname)) >= 0){ + tempname = ttempname; + ftemp = Bfdopen(fd, OWRITE); + } + if((fd = mkstemp(tactname)) >= 0){ + actname = tactname; + faction = Bfdopen(fd, OWRITE); + } + if(ftemp == 0 || faction == 0) + error("cannot open temp file"); + if(argc < 1) + error("no input file"); + infile = argv[0]; + if(infile[0] != '/' && getwd(dirbuf, sizeof dirbuf)!=nil){ + i = strlen(infile)+1+strlen(dirbuf)+1+10; + s = malloc(i); + if(s != nil){ + snprint(s, i, "%s/%s", dirbuf, infile); + cleanname(s); + infile = s; + } + } + finput = Bopen(infile, OREAD); + if(finput == 0) + error("cannot open '%s'", argv[0]); + cnamp = cnames; + + defin(0, "$end"); + extval = PRIVATE; /* tokens start in unicode 'private use' */ + defin(0, "error"); + defin(1, "$accept"); + defin(0, "$unk"); + mem = mem0; + i = 0; + + for(t = gettok(); t != MARK && t != ENDFILE;) + switch(t) { + case ';': + t = gettok(); + break; + + case START: + if(gettok() != IDENTIFIER) + error("bad %%start construction"); + start = chfind(1, tokname); + t = gettok(); + continue; + + case TYPEDEF: + if(gettok() != TYPENAME) + error("bad syntax in %%type"); + ty = numbval; + for(;;) { + t = gettok(); + switch(t) { + case IDENTIFIER: + if((t=chfind(1, tokname)) < NTBASE) { + j = TYPE(toklev[t]); + if(j != 0 && j != ty) + error("type redeclaration of token %s", + tokset[t].name); + else + SETTYPE(toklev[t], ty); + } else { + j = nontrst[t-NTBASE].value; + if(j != 0 && j != ty) + error("type redeclaration of nonterminal %s", + nontrst[t-NTBASE].name ); + else + nontrst[t-NTBASE].value = ty; + } + case ',': + continue; + case ';': + t = gettok(); + default: + break; + } + break; + } + continue; + + case UNION: + /* copy the union declaration to the output */ + cpyunion(); + t = gettok(); + continue; + + case LEFT: + case BINARY: + case RIGHT: + i++; + + case TERM: + /* nonzero means new prec. and assoc. */ + lev = t-TERM; + ty = 0; + + /* get identifiers so defined */ + t = gettok(); + + /* there is a type defined */ + if(t == TYPENAME) { + ty = numbval; + t = gettok(); + } + for(;;) { + switch(t) { + case ',': + t = gettok(); + continue; + + case ';': + break; + + case IDENTIFIER: + j = chfind(0, tokname); + if(j >= NTBASE) + error("%s defined earlier as nonterminal", tokname); + if(lev) { + if(ASSOC(toklev[j])) + error("redeclaration of precedence of %s", tokname); + SETASC(toklev[j], lev); + SETPLEV(toklev[j], i); + } + if(ty) { + if(TYPE(toklev[j])) + error("redeclaration of type of %s", tokname); + SETTYPE(toklev[j],ty); + } + t = gettok(); + if(t == NUMBER) { + tokset[j].value = numbval; + if(j < ndefout && j > 3) + error("please define type number of %s earlier", + tokset[j].name); + t = gettok(); + } + continue; + } + break; + } + continue; + + case LCURLY: + defout(0); + cpycode(); + t = gettok(); + continue; + + default: + error("syntax error"); + } + if(t == ENDFILE) + error("unexpected EOF before %%"); + + /* t is MARK */ + Bprint(ftable, "extern int yyerrflag;\n"); + Bprint(ftable, "#ifndef YYMAXDEPTH\n"); + Bprint(ftable, "#define YYMAXDEPTH 150\n"); + Bprint(ftable, "#endif\n" ); + if(!ntypes) { + Bprint(ftable, "#ifndef YYSTYPE\n"); + Bprint(ftable, "#define YYSTYPE int\n"); + Bprint(ftable, "#endif\n"); + } + Bprint(ftable, "YYSTYPE yylval;\n"); + Bprint(ftable, "YYSTYPE yyval;\n"); + + prdptr[0] = mem; + + /* added production */ + *mem++ = NTBASE; + + /* if start is 0, we will overwrite with the lhs of the first rule */ + *mem++ = start; + *mem++ = 1; + *mem++ = 0; + prdptr[1] = mem; + while((t=gettok()) == LCURLY) + cpycode(); + if(t != IDENTCOLON) + error("bad syntax on first rule"); + + if(!start) + prdptr[0][1] = chfind(1, tokname); + + /* read rules */ + while(t != MARK && t != ENDFILE) { + /* process a rule */ + rlines[nprod] = lineno; + if(t == '|') + *mem++ = *prdptr[nprod-1]; + else + if(t == IDENTCOLON) { + *mem = chfind(1, tokname); + if(*mem < NTBASE) + error("token illegal on LHS of grammar rule"); + mem++; + } else + error("illegal rule: missing semicolon or | ?"); + /* read rule body */ + t = gettok(); + + more_rule: + while(t == IDENTIFIER) { + *mem = chfind(1, tokname); + if(*mem < NTBASE) + levprd[nprod] = toklev[*mem]; + mem++; + t = gettok(); + } + if(t == PREC) { + if(gettok() != IDENTIFIER) + error("illegal %%prec syntax"); + j = chfind(2, tokname); + if(j >= NTBASE) + error("nonterminal %s illegal after %%prec", + nontrst[j-NTBASE].name); + levprd[nprod] = toklev[j]; + t = gettok(); + } + if(t == '=') { + levprd[nprod] |= ACTFLAG; + Bprint(faction, "\ncase %d:", nprod); + cpyact(mem-prdptr[nprod]-1); + Bprint(faction, " break;"); + if((t=gettok()) == IDENTIFIER) { + + /* action within rule... */ + sprint(actnm, "$$%d", nprod); + + /* make it a nonterminal */ + j = chfind(1, actnm); + + /* + * the current rule will become rule number nprod+1 + * move the contents down, and make room for the null + */ + for(p = mem; p >= prdptr[nprod]; --p) + p[2] = *p; + mem += 2; + + /* enter null production for action */ + p = prdptr[nprod]; + *p++ = j; + *p++ = -nprod; + + /* update the production information */ + levprd[nprod+1] = levprd[nprod] & ~ACTFLAG; + levprd[nprod] = ACTFLAG; + if(++nprod >= NPROD) + error("more than %d rules", NPROD); + prdptr[nprod] = p; + + /* make the action appear in the original rule */ + *mem++ = j; + + /* get some more of the rule */ + goto more_rule; + } + } + + while(t == ';') + t = gettok(); + *mem++ = -nprod; + + /* check that default action is reasonable */ + if(ntypes && !(levprd[nprod]&ACTFLAG) && nontrst[*prdptr[nprod]-NTBASE].value) { + + /* no explicit action, LHS has value */ + int tempty; + + tempty = prdptr[nprod][1]; + if(tempty < 0) + error("must return a value, since LHS has a type"); + else + if(tempty >= NTBASE) + tempty = nontrst[tempty-NTBASE].value; + else + tempty = TYPE(toklev[tempty]); + if(tempty != nontrst[*prdptr[nprod]-NTBASE].value) + error("default action causes potential type clash"); + } + nprod++; + if(nprod >= NPROD) + error("more than %d rules", NPROD); + prdptr[nprod] = mem; + levprd[nprod] = 0; + } + + /* end of all rules */ + defout(1); + + finact(); + if(t == MARK) { + Bprint(ftable, "\n#line\t%d\t\"%s\"\n", lineno, infile); + while((c=Bgetrune(finput)) != Beof) + Bputrune(ftable, c); + } + Bterm(finput); +} + +/* + * finish action routine + */ +void +finact(void) +{ + + Bterm(faction); + Bprint(ftable, "#define YYEOFCODE %d\n", 1); + Bprint(ftable, "#define YYERRCODE %d\n", 2); +} + +/* + * define s to be a terminal if t=0 + * or a nonterminal if t=1 + */ +int +defin(int nt, char *s) +{ + int val; + Rune rune; + + val = 0; + if(nt) { + nnonter++; + if(nnonter >= NNONTERM) + error("too many nonterminals, limit %d",NNONTERM); + nontrst[nnonter].name = cstash(s); + return NTBASE + nnonter; + } + + /* must be a token */ + ntokens++; + if(ntokens >= NTERMS) + error("too many terminals, limit %d", NTERMS); + tokset[ntokens].name = cstash(s); + + /* establish value for token */ + /* single character literal */ + if(s[0] == ' ') { + val = chartorune(&rune, &s[1]); + if(s[val+1] == 0) { + val = rune; + goto out; + } + } + + /* escape sequence */ + if(s[0] == ' ' && s[1] == '\\') { + if(s[3] == 0) { + /* single character escape sequence */ + switch(s[2]) { + case 'n': val = '\n'; break; + case 'r': val = '\r'; break; + case 'b': val = '\b'; break; + case 't': val = '\t'; break; + case 'f': val = '\f'; break; + case '\'': val = '\''; break; + case '"': val = '"'; break; + case '\\': val = '\\'; break; + default: error("invalid escape"); + } + goto out; + } + + /* \nnn sequence */ + if(s[2] >= '0' && s[2] <= '7') { + if(s[3] < '0' || + s[3] > '7' || + s[4] < '0' || + s[4] > '7' || + s[5] != 0) + error("illegal \\nnn construction"); + val = 64*s[2] + 8*s[3] + s[4] - 73*'0'; + if(val == 0) + error("'\\000' is illegal"); + goto out; + } + error("unknown escape"); + } + val = extval++; + +out: + tokset[ntokens].value = val; + toklev[ntokens] = 0; + return ntokens; +} + +/* + * write out the defines (at the end of the declaration section) + */ +void +defout(int last) +{ + int i, c; + char sar[NAMESIZE+10]; + + for(i=ndefout; i<=ntokens; i++) { + /* non-literals */ + c = tokset[i].name[0]; + if(c != ' ' && c != '$') { + Bprint(ftable, "#define %s %d\n", + tokset[i].name, tokset[i].value); + if(fdefine) + Bprint(fdefine, "#define\t%s\t%d\n", + tokset[i].name, tokset[i].value); + } + } + ndefout = ntokens+1; + if(last && fdebug) { + Bprint(fdebug, "char* yytoknames[] =\n{\n"); + TLOOP(i) { + if(tokset[i].name) { + chcopy(sar, tokset[i].name); + Bprint(fdebug, "\t\"%s\",\n", sar); + continue; + } + Bprint(fdebug, "\t0,\n"); + } + Bprint(fdebug, "};\n"); + } +} + +char* +cstash(char *s) +{ + char *temp; + + temp = cnamp; + do { + if(cnamp >= &cnames[cnamsz]) + error("too many characters in id's and literals"); + else + *cnamp++ = *s; + } while(*s++); + return temp; +} + +long +gettok(void) +{ + long c; + Rune rune; + int i, base, match, reserve; + static int peekline; + +begin: + reserve = 0; + lineno += peekline; + peekline = 0; + c = Bgetrune(finput); + while(c == ' ' || c == '\n' || c == '\t' || c == '\f') { + if(c == '\n') + lineno++; + c = Bgetrune(finput); + } + + /* skip comment */ + if(c == '/') { + lineno += skipcom(); + goto begin; + } + switch(c) { + case Beof: + return ENDFILE; + + case '{': + Bungetrune(finput); + return '='; + + case '<': + /* get, and look up, a type name (union member name) */ + i = 0; + while((c=Bgetrune(finput)) != '>' && c >= 0 && c != '\n') { + rune = c; + c = runetochar(&tokname[i], &rune); + if(i < NAMESIZE) + i += c; + } + if(c != '>') + error("unterminated < ... > clause"); + tokname[i] = 0; + for(i=1; i<=ntypes; i++) + if(!strcmp(typeset[i], tokname)) { + numbval = i; + return TYPENAME; + } + ntypes++; + numbval = ntypes; + typeset[numbval] = cstash(tokname); + return TYPENAME; + + case '"': + case '\'': + match = c; + tokname[0] = ' '; + i = 1; + for(;;) { + c = Bgetrune(finput); + if(c == '\n' || c <= 0) + error("illegal or missing ' or \"" ); + if(c == '\\') { + tokname[i] = '\\'; + if(i < NAMESIZE) + i++; + c = Bgetrune(finput); + } else + if(c == match) + break; + rune = c; + c = runetochar(&tokname[i], &rune); + if(i < NAMESIZE) + i += c; + } + break; + + case '%': + case '\\': + switch(c = Bgetrune(finput)) { + case '0': return TERM; + case '<': return LEFT; + case '2': return BINARY; + case '>': return RIGHT; + case '%': + case '\\': return MARK; + case '=': return PREC; + case '{': return LCURLY; + default: reserve = 1; + } + + default: + /* number */ + if(isdigit(c)) { + numbval = c-'0'; + base = (c=='0')? 8: 10; + for(c = Bgetrune(finput); isdigit(c); c = Bgetrune(finput)) + numbval = numbval*base + (c-'0'); + Bungetrune(finput); + return NUMBER; + } + if(islower(c) || isupper(c) || c=='_' || c=='.' || c=='$') { + i = 0; + while(islower(c) || isupper(c) || isdigit(c) || + c == '-' || c=='_' || c=='.' || c=='$') { + if(reserve && isupper(c)) + c += 'a'-'A'; + rune = c; + c = runetochar(&tokname[i], &rune); + if(i < NAMESIZE) + i += c; + c = Bgetrune(finput); + } + } else + return c; + Bungetrune(finput); + } + tokname[i] = 0; + + /* find a reserved word */ + if(reserve) { + for(c=0; resrv[c].name; c++) + if(strcmp(tokname, resrv[c].name) == 0) + return resrv[c].value; + error("invalid escape, or illegal reserved word: %s", tokname); + } + + /* look ahead to distinguish IDENTIFIER from IDENTCOLON */ + c = Bgetrune(finput); + while(c == ' ' || c == '\t'|| c == '\n' || c == '\f' || c == '/') { + if(c == '\n') + peekline++; + /* look for comments */ + if(c == '/') + peekline += skipcom(); + c = Bgetrune(finput); + } + if(c == ':') + return IDENTCOLON; + Bungetrune(finput); + return IDENTIFIER; +} + +/* + * determine the type of a symbol + */ +int +fdtype(int t) +{ + int v; + + if(t >= NTBASE) + v = nontrst[t-NTBASE].value; + else + v = TYPE(toklev[t]); + if(v <= 0) + error("must specify type for %s", (t>=NTBASE)? + nontrst[t-NTBASE].name: tokset[t].name); + return v; +} + +int +chfind(int t, char *s) +{ + int i; + + if(s[0] == ' ') + t = 0; + TLOOP(i) + if(!strcmp(s, tokset[i].name)) + return i; + NTLOOP(i) + if(!strcmp(s, nontrst[i].name)) + return NTBASE+i; + + /* cannot find name */ + if(t > 1) + error("%s should have been defined earlier", s); + return defin(t, s); +} + +/* + * copy the union declaration to the output, and the define file if present + */ +void +cpyunion(void) +{ + long c; + int level; + + Bprint(ftable, "\n#line\t%d\t\"%s\"\n", lineno, infile); + Bprint(ftable, "typedef union "); + if(fdefine != 0) + Bprint(fdefine, "\ntypedef union "); + + level = 0; + for(;;) { + if((c=Bgetrune(finput)) == Beof) + error("EOF encountered while processing %%union"); + Bputrune(ftable, c); + if(fdefine != 0) + Bputrune(fdefine, c); + switch(c) { + case '\n': + lineno++; + break; + case '{': + level++; + break; + case '}': + level--; + + /* we are finished copying */ + if(level == 0) { + Bprint(ftable, " YYSTYPE;\n"); + if(fdefine != 0) + Bprint(fdefine, "\tYYSTYPE;\nextern\tYYSTYPE\tyylval;\n"); + return; + } + } + } +} + +/* + * copies code between \{ and \} + */ +void +cpycode(void) +{ + + long c; + + c = Bgetrune(finput); + if(c == '\n') { + c = Bgetrune(finput); + lineno++; + } + Bprint(ftable, "\n#line\t%d\t\"%s\"\n", lineno, infile); + while(c != Beof) { + if(c == '\\') { + if((c=Bgetrune(finput)) == '}') + return; + Bputc(ftable, '\\'); + } + if(c == '%') { + if((c=Bgetrune(finput)) == '}') + return; + Bputc(ftable, '%'); + } + Bputrune(ftable, c); + if(c == '\n') + lineno++; + c = Bgetrune(finput); + } + error("eof before %%}"); +} + +/* + * skip over comments + * skipcom is called after reading a '/' + */ +int +skipcom(void) +{ + long c; + int i; + + /* i is the number of lines skipped */ + i = 0; + if(Bgetrune(finput) != '*') + error("illegal comment"); + c = Bgetrune(finput); + while(c != Beof) { + while(c == '*') + if((c=Bgetrune(finput)) == '/') + return i; + if(c == '\n') + i++; + c = Bgetrune(finput); + } + error("EOF inside comment"); + return 0; +} + +/* + * copy C action to the next ; or closing } + */ +void +cpyact(int offset) +{ + long c; + int brac, match, j, s, fnd, tok; + + Bprint(faction, "\n#line\t%d\t\"%s\"\n", lineno, infile); + brac = 0; + +loop: + c = Bgetrune(finput); +swt: + switch(c) { + case ';': + if(brac == 0) { + Bputrune(faction, c); + return; + } + goto lcopy; + + case '{': + brac++; + goto lcopy; + + case '$': + s = 1; + tok = -1; + c = Bgetrune(finput); + + /* type description */ + if(c == '<') { + Bungetrune(finput); + if(gettok() != TYPENAME) + error("bad syntax on $<ident> clause"); + tok = numbval; + c = Bgetrune(finput); + } + if(c == '$') { + Bprint(faction, "yyval"); + + /* put out the proper tag... */ + if(ntypes) { + if(tok < 0) + tok = fdtype(*prdptr[nprod]); + Bprint(faction, ".%s", typeset[tok]); + } + goto loop; + } + if(c == '-') { + s = -s; + c = Bgetrune(finput); + } + if(isdigit(c)) { + j = 0; + while(isdigit(c)) { + j = j*10 + (c-'0'); + c = Bgetrune(finput); + } + Bungetrune(finput); + j = j*s - offset; + if(j > 0) + error("Illegal use of $%d", j+offset); + + dollar: + Bprint(faction, "yypt[-%d].yyv", -j); + + /* put out the proper tag */ + if(ntypes) { + if(j+offset <= 0 && tok < 0) + error("must specify type of $%d", j+offset); + if(tok < 0) + tok = fdtype(prdptr[nprod][j+offset]); + Bprint(faction, ".%s", typeset[tok]); + } + goto loop; + } + if(isupper(c) || islower(c) || c == '_' || c == '.') { + int tok; /* tok used oustide for type info */ + + /* look for $name */ + Bungetrune(finput); + if(gettok() != IDENTIFIER) + error("$ must be followed by an identifier"); + tok = chfind(2, tokname); + if((c = Bgetrune(finput)) != '#') { + Bungetrune(finput); + fnd = -1; + } else + if(gettok() != NUMBER) { + error("# must be followed by number"); + fnd = -1; + } else + fnd = numbval; + for(j=1; j<=offset; ++j) + if(tok == prdptr[nprod][j]) { + if(--fnd <= 0) { + j -= offset; + goto dollar; + } + } + error("$name or $name#number not found"); + } + Bputc(faction, '$'); + if(s < 0 ) + Bputc(faction, '-'); + goto swt; + + case '}': + brac--; + if(brac) + goto lcopy; + Bputrune(faction, c); + return; + + case '/': + /* look for comments */ + Bputrune(faction, c); + c = Bgetrune(finput); + if(c != '*') + goto swt; + + /* it really is a comment */ + Bputrune(faction, c); + c = Bgetrune(finput); + while(c >= 0) { + while(c == '*') { + Bputrune(faction, c); + if((c=Bgetrune(finput)) == '/') + goto lcopy; + } + Bputrune(faction, c); + if(c == '\n') + lineno++; + c = Bgetrune(finput); + } + error("EOF inside comment"); + + case '\'': + /* character constant */ + match = '\''; + goto string; + + case '"': + /* character string */ + match = '"'; + + string: + Bputrune(faction, c); + while(c = Bgetrune(finput)) { + if(c == '\\') { + Bputrune(faction, c); + c = Bgetrune(finput); + if(c == '\n') + lineno++; + } else + if(c == match) + goto lcopy; + if(c == '\n') + error("newline in string or char. const."); + Bputrune(faction, c); + } + error("EOF in string or character constant"); + + case Beof: + error("action does not terminate"); + + case '\n': + lineno++; + goto lcopy; + } + +lcopy: + Bputrune(faction, c); + goto loop; +} + +void +openup(char *stem, int dflag, int vflag, int ytab, char *ytabc) +{ + char buf[256]; + + if(vflag) { + sprint(buf, "%s.%s", stem, FILEU); + foutput = Bopen(buf, OWRITE); + if(foutput == 0) + error("cannot open %s", buf); + } + if(yydebug) { + sprint(buf, "%s.%s", stem, FILEDEBUG); + if((fdebug = Bopen(buf, OWRITE)) == 0) + error("can't open %s", buf); + } + if(dflag) { + sprint(buf, "%s.%s", stem, FILED); + fdefine = Bopen(buf, OWRITE); + if(fdefine == 0) + error("can't create %s", buf); + } + if(ytab == 0) + sprint(buf, "%s.%s", stem, OFILE); + else + strcpy(buf, ytabc); + ftable = Bopen(buf, OWRITE); + if(ftable == 0) + error("cannot open table file %s", buf); +} + +/* + * print the output for the states + */ +void +output(void) +{ + int i, k, c; + Wset *u, *v; + + Bprint(ftable, "short yyexca[] =\n{"); + if(fdebug) + Bprint(fdebug, "char* yystates[] =\n{\n"); + + /* output the stuff for state i */ + SLOOP(i) { + nolook = tystate[i]!=MUSTLOOKAHEAD; + closure(i); + + /* output actions */ + nolook = 1; + aryfil(temp1, ntokens+nnonter+1, 0); + WSLOOP(wsets, u) { + c = *(u->pitem); + if(c > 1 && c < NTBASE && temp1[c] == 0) { + WSLOOP(u, v) + if(c == *(v->pitem)) + putitem(v->pitem+1, (Lkset*)0); + temp1[c] = state(c); + } else + if(c > NTBASE && temp1[(c -= NTBASE) + ntokens] == 0) + temp1[c+ntokens] = amem[indgo[i]+c]; + } + if(i == 1) + temp1[1] = ACCEPTCODE; + + /* now, we have the shifts; look at the reductions */ + lastred = 0; + WSLOOP(wsets, u) { + c = *u->pitem; + + /* reduction */ + if(c <= 0) { + lastred = -c; + TLOOP(k) + if(BIT(u->ws.lset, k)) { + if(temp1[k] == 0) + temp1[k] = c; + else + if(temp1[k] < 0) { /* reduce/reduce conflict */ + if(foutput) + Bprint(foutput, + "\n%d: reduce/reduce conflict" + " (red'ns %d and %d ) on %s", + i, -temp1[k], lastred, + symnam(k)); + if(-temp1[k] > lastred) + temp1[k] = -lastred; + zzrrconf++; + } else + /* potential shift/reduce conflict */ + precftn( lastred, k, i ); + } + } + } + wract(i); + } + + if(fdebug) + Bprint(fdebug, "};\n"); + Bprint(ftable, "};\n"); + Bprint(ftable, "#define YYNPROD %d\n", nprod); + Bprint(ftable, "#define YYPRIVATE %d\n", PRIVATE); + if(yydebug) + Bprint(ftable, "#define yydebug %s\n", yydebug); +} + +/* + * pack state i from temp1 into amem + */ +int +apack(int *p, int n) +{ + int *pp, *qq, *rr, off, *q, *r; + + /* we don't need to worry about checking because + * we will only look at entries known to be there... + * eliminate leading and trailing 0's + */ + + q = p+n; + for(pp = p, off = 0; *pp == 0 && pp <= q; ++pp, --off) + ; + /* no actions */ + if(pp > q) + return 0; + p = pp; + + /* now, find a place for the elements from p to q, inclusive */ + r = &amem[ACTSIZE-1]; + for(rr = amem; rr <= r; rr++, off++) { + for(qq = rr, pp = p; pp <= q; pp++, qq++) + if(*pp != 0) + if(*pp != *qq && *qq != 0) + goto nextk; + + /* we have found an acceptable k */ + if(pkdebug && foutput != 0) + Bprint(foutput, "off = %d, k = %d\n", off, (int)(rr-amem)); + for(qq = rr, pp = p; pp <= q; pp++, qq++) + if(*pp) { + if(qq > r) + error("action table overflow"); + if(qq > memp) + memp = qq; + *qq = *pp; + } + if(pkdebug && foutput != 0) + for(pp = amem; pp <= memp; pp += 10) { + Bprint(foutput, "\t"); + for(qq = pp; qq <= pp+9; qq++) + Bprint(foutput, "%d ", *qq); + Bprint(foutput, "\n"); + } + return(off); + nextk:; + } + error("no space in action table"); + return 0; +} + +/* + * output the gotos for the nontermninals + */ +void +go2out(void) +{ + int i, j, k, best, count, cbest, times; + + /* mark begining of gotos */ + Bprint(ftemp, "$\n"); + for(i = 1; i <= nnonter; i++) { + go2gen(i); + + /* find the best one to make default */ + best = -1; + times = 0; + + /* is j the most frequent */ + for(j = 0; j <= nstate; j++) { + if(tystate[j] == 0) + continue; + if(tystate[j] == best) + continue; + + /* is tystate[j] the most frequent */ + count = 0; + cbest = tystate[j]; + for(k = j; k <= nstate; k++) + if(tystate[k] == cbest) + count++; + if(count > times) { + best = cbest; + times = count; + } + } + + /* best is now the default entry */ + zzgobest += times-1; + for(j = 0; j <= nstate; j++) + if(tystate[j] != 0 && tystate[j] != best) { + Bprint(ftemp, "%d,%d,", j, tystate[j]); + zzgoent++; + } + + /* now, the default */ + if(best == -1) + best = 0; + zzgoent++; + Bprint(ftemp, "%d\n", best); + } +} + +/* + * output the gotos for nonterminal c + */ +void +go2gen(int c) +{ + int i, work, cc; + Item *p, *q; + + + /* first, find nonterminals with gotos on c */ + aryfil(temp1, nnonter+1, 0); + temp1[c] = 1; + work = 1; + while(work) { + work = 0; + PLOOP(0, i) + + /* cc is a nonterminal */ + if((cc=prdptr[i][1]-NTBASE) >= 0) + /* cc has a goto on c */ + if(temp1[cc] != 0) { + + /* thus, the left side of production i does too */ + cc = *prdptr[i]-NTBASE; + if(temp1[cc] == 0) { + work = 1; + temp1[cc] = 1; + } + } + } + + /* now, we have temp1[c] = 1 if a goto on c in closure of cc */ + if(g2debug && foutput != 0) { + Bprint(foutput, "%s: gotos on ", nontrst[c].name); + NTLOOP(i) + if(temp1[i]) + Bprint(foutput, "%s ", nontrst[i].name); + Bprint(foutput, "\n"); + } + + /* now, go through and put gotos into tystate */ + aryfil(tystate, nstate, 0); + SLOOP(i) + ITMLOOP(i, p, q) + if((cc = *p->pitem) >= NTBASE) + /* goto on c is possible */ + if(temp1[cc-NTBASE]) { + tystate[i] = amem[indgo[i]+c]; + break; + } +} + +/* + * decide a shift/reduce conflict by precedence. + * r is a rule number, t a token number + * the conflict is in state s + * temp1[t] is changed to reflect the action + */ +void +precftn(int r, int t, int s) +{ + int lp, lt, action; + + lp = levprd[r]; + lt = toklev[t]; + if(PLEVEL(lt) == 0 || PLEVEL(lp) == 0) { + + /* conflict */ + if(foutput != 0) + Bprint(foutput, + "\n%d: shift/reduce conflict (shift %d(%d), red'n %d(%d)) on %s", + s, temp1[t], PLEVEL(lt), r, PLEVEL(lp), symnam(t)); + zzsrconf++; + return; + } + if(PLEVEL(lt) == PLEVEL(lp)) + action = ASSOC(lt); + else + if(PLEVEL(lt) > PLEVEL(lp)) + action = RASC; /* shift */ + else + action = LASC; /* reduce */ + switch(action) { + case BASC: /* error action */ + temp1[t] = ERRCODE; + break; + case LASC: /* reduce */ + temp1[t] = -r; + break; + } +} + +/* + * output state i + * temp1 has the actions, lastred the default + */ +void +wract(int i) +{ + int p, p0, p1, ntimes, tred, count, j, flag; + + /* find the best choice for lastred */ + lastred = 0; + ntimes = 0; + TLOOP(j) { + if(temp1[j] >= 0) + continue; + if(temp1[j]+lastred == 0) + continue; + /* count the number of appearances of temp1[j] */ + count = 0; + tred = -temp1[j]; + levprd[tred] |= REDFLAG; + TLOOP(p) + if(temp1[p]+tred == 0) + count++; + if(count > ntimes) { + lastred = tred; + ntimes = count; + } + } + + /* + * for error recovery, arrange that, if there is a shift on the + * error recovery token, `error', that the default be the error action + */ + if(temp1[2] > 0) + lastred = 0; + + /* clear out entries in temp1 which equal lastred */ + TLOOP(p) + if(temp1[p]+lastred == 0) + temp1[p] = 0; + + wrstate(i); + defact[i] = lastred; + flag = 0; + TLOOP(p0) + if((p1=temp1[p0]) != 0) { + if(p1 < 0) { + p1 = -p1; + goto exc; + } + if(p1 == ACCEPTCODE) { + p1 = -1; + goto exc; + } + if(p1 == ERRCODE) { + p1 = 0; + exc: + if(flag++ == 0) + Bprint(ftable, "-1, %d,\n", i); + Bprint(ftable, "\t%d, %d,\n", p0, p1); + zzexcp++; + continue; + } + Bprint(ftemp, "%d,%d,", p0, p1); + zzacent++; + } + if(flag) { + defact[i] = -2; + Bprint(ftable, "\t-2, %d,\n", lastred); + } + Bprint(ftemp, "\n"); +} + +/* + * writes state i + */ +void +wrstate(int i) +{ + int j0, j1; + Item *pp, *qq; + Wset *u; + + if(fdebug) { + if(lastred) { + Bprint(fdebug, " 0, /*%d*/\n", i); + } else { + Bprint(fdebug, " \""); + ITMLOOP(i, pp, qq) + Bprint(fdebug, "%s\\n", writem(pp->pitem)); + if(tystate[i] == MUSTLOOKAHEAD) + WSLOOP(wsets + (pstate[i+1] - pstate[i]), u) + if(*u->pitem < 0) + Bprint(fdebug, "%s\\n", writem(u->pitem)); + Bprint(fdebug, "\", /*%d*/\n", i); + } + } + if(foutput == 0) + return; + Bprint(foutput, "\nstate %d\n", i); + ITMLOOP(i, pp, qq) + Bprint(foutput, "\t%s\n", writem(pp->pitem)); + if(tystate[i] == MUSTLOOKAHEAD) + /* print out empty productions in closure */ + WSLOOP(wsets+(pstate[i+1]-pstate[i]), u) + if(*u->pitem < 0) + Bprint(foutput, "\t%s\n", writem(u->pitem)); + + /* check for state equal to another */ + TLOOP(j0) + if((j1=temp1[j0]) != 0) { + Bprint(foutput, "\n\t%s ", symnam(j0)); + /* shift, error, or accept */ + if(j1 > 0) { + if(j1 == ACCEPTCODE) + Bprint(foutput, "accept"); + else + if(j1 == ERRCODE) + Bprint(foutput, "error"); + else + Bprint(foutput, "shift %d", j1); + } else + Bprint(foutput, "reduce %d (src line %d)", -j1, rlines[-j1]); + } + + /* output the final production */ + if(lastred) + Bprint(foutput, "\n\t. reduce %d (src line %d)\n\n", + lastred, rlines[lastred]); + else + Bprint(foutput, "\n\t. error\n\n"); + + /* now, output nonterminal actions */ + j1 = ntokens; + for(j0 = 1; j0 <= nnonter; j0++) { + j1++; + if(temp1[j1]) + Bprint(foutput, "\t%s goto %d\n", symnam(j0+NTBASE), temp1[j1]); + } +} + +void +warray(char *s, int *v, int n) +{ + int i; + + Bprint(ftable, "short %s[] =\n{", s); + for(i=0;;) { + if(i%10 == 0) + Bprint(ftable, "\n"); + Bprint(ftable, "%4d", v[i]); + i++; + if(i >= n) { + Bprint(ftable, "\n};\n"); + break; + } + Bprint(ftable, ","); + } +} + +/* + * in order to free up the mem and amem arrays for the optimizer, + * and still be able to output yyr1, etc., after the sizes of + * the action array is known, we hide the nonterminals + * derived by productions in levprd. + */ + +void +hideprod(void) +{ + int i, j; + + j = 0; + levprd[0] = 0; + PLOOP(1, i) { + if(!(levprd[i] & REDFLAG)) { + j++; + if(foutput != 0) + Bprint(foutput, "Rule not reduced: %s\n", writem(prdptr[i])); + } + levprd[i] = *prdptr[i] - NTBASE; + } + if(j) + print("%d rules never reduced\n", j); +} + +void +callopt(void) +{ + int i, *p, j, k, *q; + + /* read the arrays from tempfile and set parameters */ + finput = Bopen(tempname, OREAD); + if(finput == 0) + error("optimizer cannot open tempfile"); + + pgo[0] = 0; + temp1[0] = 0; + nstate = 0; + nnonter = 0; + for(;;) { + switch(gtnm()) { + case '\n': + nstate++; + pmem--; + temp1[nstate] = pmem - mem0; + case ',': + continue; + case '$': + break; + default: + error("bad tempfile"); + } + break; + } + + pmem--; + temp1[nstate] = yypgo[0] = pmem - mem0; + for(;;) { + switch(gtnm()) { + case '\n': + nnonter++; + yypgo[nnonter] = pmem-mem0; + case ',': + continue; + case -1: + break; + default: + error("bad tempfile"); + } + break; + } + pmem--; + yypgo[nnonter--] = pmem - mem0; + for(i = 0; i < nstate; i++) { + k = 32000; + j = 0; + q = mem0 + temp1[i+1]; + for(p = mem0 + temp1[i]; p < q ; p += 2) { + if(*p > j) + j = *p; + if(*p < k) + k = *p; + } + /* nontrivial situation */ + if(k <= j) { + /* j is now the range */ +/* j -= k; *//* call scj */ + if(k > maxoff) + maxoff = k; + } + tystate[i] = (temp1[i+1]-temp1[i]) + 2*j; + if(j > maxspr) + maxspr = j; + } + + /* initialize ggreed table */ + for(i = 1; i <= nnonter; i++) { + ggreed[i] = 1; + j = 0; + + /* minimum entry index is always 0 */ + q = mem0 + yypgo[i+1] - 1; + for(p = mem0+yypgo[i]; p < q ; p += 2) { + ggreed[i] += 2; + if(*p > j) + j = *p; + } + ggreed[i] = ggreed[i] + 2*j; + if(j > maxoff) + maxoff = j; + } + + /* now, prepare to put the shift actions into the amem array */ + for(i = 0; i < ACTSIZE; i++) + amem[i] = 0; + maxa = amem; + for(i = 0; i < nstate; i++) { + if(tystate[i] == 0 && adb > 1) + Bprint(ftable, "State %d: null\n", i); + indgo[i] = YYFLAG1; + } + while((i = nxti()) != NOMORE) + if(i >= 0) + stin(i); + else + gin(-i); + + /* print amem array */ + if(adb > 2 ) + for(p = amem; p <= maxa; p += 10) { + Bprint(ftable, "%4d ", (int)(p-amem)); + for(i = 0; i < 10; ++i) + Bprint(ftable, "%4d ", p[i]); + Bprint(ftable, "\n"); + } + + /* write out the output appropriate to the language */ + aoutput(); + osummary(); + ZAPFILE(tempname); +} + +void +gin(int i) +{ + int *p, *r, *s, *q1, *q2; + + /* enter gotos on nonterminal i into array amem */ + ggreed[i] = 0; + + q2 = mem0+ yypgo[i+1] - 1; + q1 = mem0 + yypgo[i]; + + /* now, find amem place for it */ + for(p = amem; p < &amem[ACTSIZE]; p++) { + if(*p) + continue; + for(r = q1; r < q2; r += 2) { + s = p + *r + 1; + if(*s) + goto nextgp; + if(s > maxa) + if((maxa = s) > &amem[ACTSIZE]) + error("a array overflow"); + } + /* we have found amem spot */ + *p = *q2; + if(p > maxa) + if((maxa = p) > &amem[ACTSIZE]) + error("a array overflow"); + for(r = q1; r < q2; r += 2) { + s = p + *r + 1; + *s = r[1]; + } + pgo[i] = p-amem; + if(adb > 1) + Bprint(ftable, "Nonterminal %d, entry at %d\n", i, pgo[i]); + return; + + nextgp:; + } + error("cannot place goto %d\n", i); +} + +void +stin(int i) +{ + int *r, *s, n, flag, j, *q1, *q2; + + tystate[i] = 0; + + /* enter state i into the amem array */ + q2 = mem0+temp1[i+1]; + q1 = mem0+temp1[i]; + /* find an acceptable place */ + for(n = -maxoff; n < ACTSIZE; n++) { + flag = 0; + for(r = q1; r < q2; r += 2) { + if((s = *r + n + amem) < amem) + goto nextn; + if(*s == 0) + flag++; + else + if(*s != r[1]) + goto nextn; + } + + /* check that the position equals another only if the states are identical */ + for(j=0; j<nstate; j++) { + if(indgo[j] == n) { + + /* we have some disagreement */ + if(flag) + goto nextn; + if(temp1[j+1]+temp1[i] == temp1[j]+temp1[i+1]) { + + /* states are equal */ + indgo[i] = n; + if(adb > 1) + Bprint(ftable, + "State %d: entry at %d equals state %d\n", + i, n, j); + return; + } + + /* we have some disagreement */ + goto nextn; + } + } + + for(r = q1; r < q2; r += 2) { + if((s = *r+n+amem) >= &amem[ACTSIZE]) + error("out of space in optimizer a array"); + if(s > maxa) + maxa = s; + if(*s != 0 && *s != r[1]) + error("clobber of a array, pos'n %d, by %d", s-amem, r[1]); + *s = r[1]; + } + indgo[i] = n; + if(adb > 1) + Bprint(ftable, "State %d: entry at %d\n", i, indgo[i]); + return; + nextn:; + } + error("Error; failure to place state %d\n", i); +} + +/* + * finds the next i + */ +int +nxti(void) +{ + int i, max, maxi; + + max = 0; + maxi = 0; + for(i = 1; i <= nnonter; i++) + if(ggreed[i] >= max) { + max = ggreed[i]; + maxi = -i; + } + for(i = 0; i < nstate; ++i) + if(tystate[i] >= max) { + max = tystate[i]; + maxi = i; + } + if(nxdb) + Bprint(ftable, "nxti = %d, max = %d\n", maxi, max); + if(max == 0) + return NOMORE; + return maxi; +} + +/* + * write summary + */ +void +osummary(void) +{ + + int i, *p; + + if(foutput == 0) + return; + i = 0; + for(p = maxa; p >= amem; p--) + if(*p == 0) + i++; + + Bprint(foutput, "Optimizer space used: input %d/%d, output %d/%d\n", + (int)(pmem-mem0+1), MEMSIZE, (int)(maxa-amem+1), ACTSIZE); + Bprint(foutput, "%d table entries, %d zero\n", (int)(maxa-amem+1), i); + Bprint(foutput, "maximum spread: %d, maximum offset: %d\n", maxspr, maxoff); +} + +/* + * this version is for C + * write out the optimized parser + */ +void +aoutput(void) +{ + Bprint(ftable, "#define\tYYLAST\t%d\n", (int)(maxa-amem+1)); + arout("yyact", amem, (maxa-amem)+1); + arout("yypact", indgo, nstate); + arout("yypgo", pgo, nnonter+1); +} + +void +arout(char *s, int *v, int n) +{ + int i; + + Bprint(ftable, "short %s[] =\n{", s); + for(i = 0; i < n;) { + if(i%10 == 0) + Bprint(ftable, "\n"); + Bprint(ftable, "%4d", v[i]); + i++; + if(i == n) + Bprint(ftable, "\n};\n"); + else + Bprint(ftable, ","); + } +} + +/* + * read and convert an integer from the standard input + * return the terminating character + * blanks, tabs, and newlines are ignored + */ +int +gtnm(void) +{ + int sign, val, c; + + sign = 0; + val = 0; + while((c=Bgetrune(finput)) != Beof) { + if(isdigit(c)) { + val = val*10 + c-'0'; + continue; + } + if(c == '-') { + sign = 1; + continue; + } + break; + } + if(sign) + val = -val; + *pmem++ = val; + if(pmem >= &mem0[MEMSIZE]) + error("out of space"); + return c; +} |