aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/pr.c
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2004-04-21 22:37:11 +0000
committerrsc <devnull@localhost>2004-04-21 22:37:11 +0000
commit53db2d0f6bfc971f44183f360f3db5a72edd948a (patch)
tree8b219cc7e666cd924e5eda263698b8263a782727 /src/cmd/pr.c
parentf80f53a9424c26f28c8819558541d19cd690a440 (diff)
downloadplan9port-53db2d0f6bfc971f44183f360f3db5a72edd948a.tar.gz
plan9port-53db2d0f6bfc971f44183f360f3db5a72edd948a.tar.bz2
plan9port-53db2d0f6bfc971f44183f360f3db5a72edd948a.zip
why not?
Diffstat (limited to 'src/cmd/pr.c')
-rw-r--r--src/cmd/pr.c663
1 files changed, 663 insertions, 0 deletions
diff --git a/src/cmd/pr.c b/src/cmd/pr.c
new file mode 100644
index 00000000..934cfa5e
--- /dev/null
+++ b/src/cmd/pr.c
@@ -0,0 +1,663 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+
+/*
+ * PR command (print files in pages and columns, with headings)
+ * 2+head+2+page[56]+5
+ */
+
+#define ISPRINT(c) ((c) >= ' ')
+#define ESC '\033'
+#define LENGTH 66
+#define LINEW 72
+#define NUMW 5
+#define MARGIN 10
+#define DEFTAB 8
+#define NFILES 10
+#define HEAD "%12.12s %4.4s %s Page %d\n\n\n", date+4, date+24, head, Page
+#define TOLOWER(c) (isupper(c) ? tolower(c) : c) /* ouch! */
+#define cerror(S) fprint(2, "pr: %s", S)
+#define STDINNAME() nulls
+#define TTY "/dev/cons", 0
+#define PROMPT() fprint(2, "\a") /* BEL */
+#define TABS(N,C) if((N = intopt(argv, &C)) < 0) N = DEFTAB
+#define ETABS (Inpos % Etabn)
+#define ITABS (Itabn > 0 && Nspace > 1 && Nspace >= (nc = Itabn - Outpos % Itabn))
+#define NSEPC '\t'
+#define EMPTY 14 /* length of " -- empty file" */
+
+typedef struct Fils Fils;
+typedef struct Colp* Colp;
+typedef struct Err Err;
+
+struct Fils
+{
+ Biobuf* f_f;
+ char* f_name;
+ long f_nextc;
+};
+struct Colp
+{
+ Rune* c_ptr;
+ Rune* c_ptr0;
+ long c_lno;
+};
+struct Err
+{
+ Err* e_nextp;
+ char* e_mess;
+};
+
+int Balance = 0;
+Biobuf bout;
+Rune* Bufend;
+Rune* Buffer = 0;
+int C = '\0';
+Colp Colpts;
+int Colw;
+int Dblspace = 1;
+Err* err = 0;
+int error = 0;
+int Etabc = '\t';
+int Etabn = 0;
+Fils* Files;
+int Formfeed = 0;
+int Fpage = 1;
+char* Head = 0;
+int Inpos;
+int Itabc = '\t';
+int Itabn = 0;
+Err* Lasterr = (Err*)&err;
+int Lcolpos;
+int Len = LENGTH;
+int Line;
+int Linew = 0;
+long Lnumb = 0;
+int Margin = MARGIN;
+int Multi = 0;
+int Ncols = 1;
+int Nfiles = 0;
+int Nsepc = NSEPC;
+int Nspace;
+char nulls[] = "";
+int Numw;
+int Offset = 0;
+int Outpos;
+int Padodd;
+int Page;
+int Pcolpos;
+int Plength;
+int Sepc = 0;
+
+extern int atoix(char**);
+extern void balance(int);
+extern void die(char*);
+extern void errprint(void);
+extern char* ffiler(char*);
+extern int findopt(int, char**);
+extern int get(int);
+extern void* getspace(ulong);
+extern int intopt(char**, int*);
+extern void main(int, char**);
+extern Biobuf* mustopen(char*, Fils*);
+extern void nexbuf(void);
+extern int pr(char*);
+extern void put(long);
+extern void putpage(void);
+extern void putspace(void);
+
+/*
+ * return date file was last modified
+ */
+char*
+getdate(void)
+{
+ static char *now = 0;
+ static Dir *sbuf;
+ ulong mtime;
+
+ if(Nfiles > 1 || Files->f_name == nulls) {
+ if(now == 0) {
+ mtime = time(0);
+ now = ctime(mtime);
+ }
+ return now;
+ }
+ mtime = 0;
+ sbuf = dirstat(Files->f_name);
+ if(sbuf){
+ mtime = sbuf->mtime;
+ free(sbuf);
+ }
+ return ctime(mtime);
+}
+
+char*
+ffiler(char *s)
+{
+ return smprint("can't open %s\n", s);
+}
+
+void
+main(int argc, char *argv[])
+{
+ Fils fstr[NFILES];
+ int nfdone = 0;
+
+ Binit(&bout, 1, OWRITE);
+ Files = fstr;
+ for(argc = findopt(argc, argv); argc > 0; --argc, ++argv)
+ if(Multi == 'm') {
+ if(Nfiles >= NFILES - 1)
+ die("too many files");
+ if(mustopen(*argv, &Files[Nfiles++]) == 0)
+ nfdone++; /* suppress printing */
+ } else {
+ if(pr(*argv))
+ Bterm(Files->f_f);
+ nfdone++;
+ }
+ if(!nfdone) /* no files named, use stdin */
+ pr(nulls); /* on GCOS, use current file, if any */
+ errprint(); /* print accumulated error reports */
+ exits(error? "error": 0);
+}
+
+int
+findopt(int argc, char *argv[])
+{
+ char **eargv = argv;
+ int eargc = 0, c;
+
+ while(--argc > 0) {
+ switch(c = **++argv) {
+ case '-':
+ if((c = *++*argv) == '\0')
+ break;
+ case '+':
+ do {
+ if(isdigit(c)) {
+ --*argv;
+ Ncols = atoix(argv);
+ } else
+ switch(c = TOLOWER(c)) {
+ case '+':
+ if((Fpage = atoix(argv)) < 1)
+ Fpage = 1;
+ continue;
+ case 'd':
+ Dblspace = 2;
+ continue;
+ case 'e':
+ TABS(Etabn, Etabc);
+ continue;
+ case 'f':
+ Formfeed++;
+ continue;
+ case 'h':
+ if(--argc > 0)
+ Head = argv[1];
+ continue;
+ case 'i':
+ TABS(Itabn, Itabc);
+ continue;
+ case 'l':
+ Len = atoix(argv);
+ continue;
+ case 'a':
+ case 'm':
+ Multi = c;
+ continue;
+ case 'o':
+ Offset = atoix(argv);
+ continue;
+ case 's':
+ if((Sepc = (*argv)[1]) != '\0')
+ ++*argv;
+ else
+ Sepc = '\t';
+ continue;
+ case 't':
+ Margin = 0;
+ continue;
+ case 'w':
+ Linew = atoix(argv);
+ continue;
+ case 'n':
+ Lnumb++;
+ if((Numw = intopt(argv, &Nsepc)) <= 0)
+ Numw = NUMW;
+ case 'b':
+ Balance = 1;
+ continue;
+ case 'p':
+ Padodd = 1;
+ continue;
+ default:
+ die("bad option");
+ }
+ } while((c = *++*argv) != '\0');
+ if(Head == argv[1])
+ argv++;
+ continue;
+ }
+ *eargv++ = *argv;
+ eargc++;
+ }
+ if(Len == 0)
+ Len = LENGTH;
+ if(Len <= Margin)
+ Margin = 0;
+ Plength = Len - Margin/2;
+ if(Multi == 'm')
+ Ncols = eargc;
+ switch(Ncols) {
+ case 0:
+ Ncols = 1;
+ case 1:
+ break;
+ default:
+ if(Etabn == 0) /* respect explicit tab specification */
+ Etabn = DEFTAB;
+ }
+ if(Linew == 0)
+ Linew = Ncols != 1 && Sepc == 0? LINEW: 512;
+ if(Lnumb)
+ Linew -= Multi == 'm'? Numw: Numw*Ncols;
+ if((Colw = (Linew - Ncols + 1)/Ncols) < 1)
+ die("width too small");
+ if(Ncols != 1 && Multi == 0) {
+ ulong buflen = ((ulong)(Plength/Dblspace + 1))*(Linew+1)*sizeof(char);
+ Buffer = getspace(buflen*sizeof(*Buffer));
+ Bufend = &Buffer[buflen];
+ Colpts = getspace((Ncols+1)*sizeof(*Colpts));
+ }
+ return eargc;
+}
+
+int
+intopt(char *argv[], int *optp)
+{
+ int c;
+
+ if((c = (*argv)[1]) != '\0' && !isdigit(c)) {
+ *optp = c;
+ (*argv)++;
+ }
+ c = atoix(argv);
+ return c != 0? c: -1;
+}
+
+int
+pr(char *name)
+{
+ char *date = 0, *head = 0;
+
+ if(Multi != 'm' && mustopen(name, &Files[0]) == 0)
+ return 0;
+ if(Buffer)
+ Bungetc(Files->f_f);
+ if(Lnumb)
+ Lnumb = 1;
+ for(Page = 0;; putpage()) {
+ if(C == -1)
+ break;
+ if(Buffer)
+ nexbuf();
+ Inpos = 0;
+ if(get(0) == -1)
+ break;
+ Bflush(&bout);
+ Page++;
+ if(Page >= Fpage) {
+ if(Margin == 0)
+ continue;
+ if(date == 0)
+ date = getdate();
+ if(head == 0)
+ head = Head != 0 ? Head :
+ Nfiles < 2? Files->f_name: nulls;
+ Bprint(&bout, "\n\n");
+ Nspace = Offset;
+ putspace();
+ Bprint(&bout, HEAD);
+ }
+ }
+ if(Padodd && (Page&1) == 1) {
+ Line = 0;
+ if(Formfeed)
+ put('\f');
+ else
+ while(Line < Len)
+ put('\n');
+ }
+ C = '\0';
+ return 1;
+}
+
+void
+putpage(void)
+{
+ int colno;
+
+ for(Line = Margin/2;; get(0)) {
+ for(Nspace = Offset, colno = 0, Outpos = 0; C != '\f';) {
+ if(Lnumb && C != -1 && (colno == 0 || Multi == 'a')) {
+ if(Page >= Fpage) {
+ putspace();
+ Bprint(&bout, "%*ld", Numw, Buffer?
+ Colpts[colno].c_lno++: Lnumb);
+ Outpos += Numw;
+ put(Nsepc);
+ }
+ Lnumb++;
+ }
+ for(Lcolpos=0, Pcolpos=0; C!='\n' && C!='\f' && C!=-1; get(colno))
+ put(C);
+ if(C==-1 || ++colno==Ncols || C=='\n' && get(colno)==-1)
+ break;
+ if(Sepc)
+ put(Sepc);
+ else
+ if((Nspace += Colw - Lcolpos + 1) < 1)
+ Nspace = 1;
+ }
+ /*
+ if(C == -1) {
+ if(Margin != 0)
+ break;
+ if(colno != 0)
+ put('\n');
+ return;
+ }
+ */
+ if(C == -1 && colno == 0) {
+ if(Margin != 0)
+ break;
+ return;
+ }
+ if(C == '\f')
+ break;
+ put('\n');
+ if(Dblspace == 2 && Line < Plength)
+ put('\n');
+ if(Line >= Plength)
+ break;
+ }
+ if(Formfeed)
+ put('\f');
+ else
+ while(Line < Len)
+ put('\n');
+}
+
+void
+nexbuf(void)
+{
+ Rune *s = Buffer;
+ Colp p = Colpts;
+ int j, c, bline = 0;
+
+ for(;;) {
+ p->c_ptr0 = p->c_ptr = s;
+ if(p == &Colpts[Ncols])
+ return;
+ (p++)->c_lno = Lnumb + bline;
+ for(j = (Len - Margin)/Dblspace; --j >= 0; bline++)
+ for(Inpos = 0;;) {
+ if((c = Bgetrune(Files->f_f)) == -1) {
+ for(*s = -1; p <= &Colpts[Ncols]; p++)
+ p->c_ptr0 = p->c_ptr = s;
+ if(Balance)
+ balance(bline);
+ return;
+ }
+ if(ISPRINT(c))
+ Inpos++;
+ if(Inpos <= Colw || c == '\n') {
+ *s = c;
+ if(++s >= Bufend)
+ die("page-buffer overflow");
+ }
+ if(c == '\n')
+ break;
+ switch(c) {
+ case '\b':
+ if(Inpos == 0)
+ s--;
+ case ESC:
+ if(Inpos > 0)
+ Inpos--;
+ }
+ }
+ }
+}
+
+/*
+ * line balancing for last page
+ */
+void
+balance(int bline)
+{
+ Rune *s = Buffer;
+ Colp p = Colpts;
+ int colno = 0, j, c, l;
+
+ c = bline % Ncols;
+ l = (bline + Ncols - 1)/Ncols;
+ bline = 0;
+ do {
+ for(j = 0; j < l; ++j)
+ while(*s++ != '\n')
+ ;
+ (++p)->c_lno = Lnumb + (bline += l);
+ p->c_ptr0 = p->c_ptr = s;
+ if(++colno == c)
+ l--;
+ } while(colno < Ncols - 1);
+}
+
+int
+get(int colno)
+{
+ static int peekc = 0;
+ Colp p;
+ Fils *q;
+ long c;
+
+ if(peekc) {
+ peekc = 0;
+ c = Etabc;
+ } else
+ if(Buffer) {
+ p = &Colpts[colno];
+ if(p->c_ptr >= (p+1)->c_ptr0)
+ c = -1;
+ else
+ if((c = *p->c_ptr) != -1)
+ p->c_ptr++;
+ } else
+ if((c = (q = &Files[Multi == 'a'? 0: colno])->f_nextc) == -1) {
+ for(q = &Files[Nfiles]; --q >= Files && q->f_nextc == -1;)
+ ;
+ if(q >= Files)
+ c = '\n';
+ } else
+ q->f_nextc = Bgetrune(q->f_f);
+ if(Etabn != 0 && c == Etabc) {
+ Inpos++;
+ peekc = ETABS;
+ c = ' ';
+ } else
+ if(ISPRINT(c))
+ Inpos++;
+ else
+ switch(c) {
+ case '\b':
+ case ESC:
+ if(Inpos > 0)
+ Inpos--;
+ break;
+ case '\f':
+ if(Ncols == 1)
+ break;
+ c = '\n';
+ case '\n':
+ case '\r':
+ Inpos = 0;
+ }
+ return C = c;
+}
+
+void
+put(long c)
+{
+ int move;
+
+ switch(c) {
+ case ' ':
+ Nspace++;
+ Lcolpos++;
+ return;
+ case '\b':
+ if(Lcolpos == 0)
+ return;
+ if(Nspace > 0) {
+ Nspace--;
+ Lcolpos--;
+ return;
+ }
+ if(Lcolpos > Pcolpos) {
+ Lcolpos--;
+ return;
+ }
+ case ESC:
+ move = -1;
+ break;
+ case '\n':
+ Line++;
+ case '\r':
+ case '\f':
+ Pcolpos = 0;
+ Lcolpos = 0;
+ Nspace = 0;
+ Outpos = 0;
+ default:
+ move = (ISPRINT(c) != 0);
+ }
+ if(Page < Fpage)
+ return;
+ if(Lcolpos > 0 || move > 0)
+ Lcolpos += move;
+ if(Lcolpos <= Colw) {
+ putspace();
+ Bputrune(&bout, c);
+ Pcolpos = Lcolpos;
+ Outpos += move;
+ }
+}
+
+void
+putspace(void)
+{
+ int nc;
+
+ for(; Nspace > 0; Outpos += nc, Nspace -= nc)
+ if(ITABS)
+ Bputc(&bout, Itabc);
+ else {
+ nc = 1;
+ Bputc(&bout, ' ');
+ }
+}
+
+int
+atoix(char **p)
+{
+ int n = 0, c;
+
+ while(isdigit(c = *++*p))
+ n = 10*n + c - '0';
+ (*p)--;
+ return n;
+}
+
+/*
+ * Defer message about failure to open file to prevent messing up
+ * alignment of page with tear perforations or form markers.
+ * Treat empty file as special case and report as diagnostic.
+ */
+Biobuf*
+mustopen(char *s, Fils *f)
+{
+ char *tmp;
+
+ if(*s == '\0') {
+ f->f_name = STDINNAME();
+ f->f_f = malloc(sizeof(Biobuf));
+ if(f->f_f == 0)
+ cerror("no memory");
+ Binit(f->f_f, 0, OREAD);
+ } else
+ if((f->f_f = Bopen(f->f_name = s, OREAD)) == 0) {
+ tmp = ffiler(f->f_name);
+ s = strcpy((char*)getspace(strlen(tmp) + 1), tmp);
+ free(tmp);
+ }
+ if(f->f_f != 0) {
+ if((f->f_nextc = Bgetrune(f->f_f)) >= 0 || Multi == 'm')
+ return f->f_f;
+ sprint(s = (char*)getspace(strlen(f->f_name) + 1 + EMPTY),
+ "%s -- empty file\n", f->f_name);
+ Bterm(f->f_f);
+ }
+ error = 1;
+ cerror(s);
+ fprint(2, "\n");
+ return 0;
+}
+
+void*
+getspace(ulong n)
+{
+ void *t;
+
+ if((t = malloc(n)) == 0)
+ die("out of space");
+ return t;
+}
+
+void
+die(char *s)
+{
+ error++;
+ errprint();
+ cerror(s);
+ Bputc(&bout, '\n');
+ exits("error");
+}
+
+/*
+void
+onintr(void)
+{
+ error++;
+ errprint();
+ exits("error");
+}
+/**/
+
+/*
+ * print accumulated error reports
+ */
+void
+errprint(void)
+{
+ Bflush(&bout);
+ for(; err != 0; err = err->e_nextp) {
+ cerror(err->e_mess);
+ fprint(2, "\n");
+ }
+}