URI:
       tpr.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tpr.c (11219B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <bio.h>
            4 #include <ctype.h>
            5 
            6 /*
            7  *        PR command (print files in pages and columns, with headings)
            8  *        2+head+2+page[56]+5
            9  */
           10 
           11 #define err                pr_err
           12 #define        ISPRINT(c)        ((c) >= ' ')
           13 #define ESC                '\033'
           14 #define LENGTH                66
           15 #define LINEW                72
           16 #define NUMW                5
           17 #define MARGIN                10
           18 #define DEFTAB                8
           19 #define NFILES                10
           20 #define HEAD                "%12.12s %4.4s  %s Page %d\n\n\n", date+4, date+24, head, Page
           21 #define TOLOWER(c)        (isupper(c) ? tolower(c) : c)        /* ouch! */
           22 #define cerror(S)        fprint(2, "pr: %s", S)
           23 #define STDINNAME()        nulls
           24 #define TTY                "/dev/cons", 0
           25 #define PROMPT()        fprint(2, "\a") /* BEL */
           26 #define TABS(N,C)        if((N = intopt(argv, &C)) < 0) N = DEFTAB
           27 #define ETABS                (Inpos % Etabn)
           28 #define ITABS                (Itabn > 0 && Nspace > 1 && Nspace >= (nc = Itabn - Outpos % Itabn))
           29 #define NSEPC                '\t'
           30 #define EMPTY                14        /* length of " -- empty file" */
           31 
           32 typedef        struct        Fils        Fils;
           33 typedef        struct        Colp*        Colp;
           34 typedef        struct        Err        Err;
           35 
           36 struct        Fils
           37 {
           38         Biobuf*        f_f;
           39         char*        f_name;
           40         long        f_nextc;
           41 };
           42 struct        Colp
           43 {
           44         Rune*        c_ptr;
           45         Rune*        c_ptr0;
           46         long        c_lno;
           47 };
           48 struct        Err
           49 {
           50         Err*        e_nextp;
           51         char*        e_mess;
           52 };
           53 
           54 int        Balance = 0;
           55 Biobuf        bout;
           56 Rune*        Bufend;
           57 Rune*        Buffer = 0;
           58 int        C = '\0';
           59 Colp        Colpts;
           60 int        Colw;
           61 int        Dblspace = 1;
           62 Err*        err = 0;
           63 int        error = 0;
           64 int        Etabc = '\t';
           65 int        Etabn = 0;
           66 Fils*        Files;
           67 int        Formfeed = 0;
           68 int        Fpage = 1;
           69 char*        Head = 0;
           70 int        Inpos;
           71 int        Itabc = '\t';
           72 int        Itabn = 0;
           73 Err*        Lasterr = (Err*)&err;
           74 int        Lcolpos;
           75 int        Len = LENGTH;
           76 int        Line;
           77 int        Linew = 0;
           78 long        Lnumb = 0;
           79 int        Margin = MARGIN;
           80 int        Multi = 0;
           81 int        Ncols = 1;
           82 int        Nfiles = 0;
           83 int        Nsepc = NSEPC;
           84 int        Nspace;
           85 char        nulls[] = "";
           86 int        Numw;
           87 int        Offset = 0;
           88 int        Outpos;
           89 int        Padodd;
           90 int        Page;
           91 int        Pcolpos;
           92 int        Plength;
           93 int        Sepc = 0;
           94 
           95 extern        int        atoix(char**);
           96 extern        void        balance(int);
           97 extern        void        die(char*);
           98 extern        void        errprint(void);
           99 extern        char*        ffiler(char*);
          100 extern        int        findopt(int, char**);
          101 extern        int        get(int);
          102 extern        void*        getspace(ulong);
          103 extern        int        intopt(char**, int*);
          104 extern        void        main(int, char**);
          105 extern        Biobuf*        mustopen(char*, Fils*);
          106 extern        void        nexbuf(void);
          107 extern        int        pr(char*);
          108 extern        void        put(long);
          109 extern        void        putpage(void);
          110 extern        void        putspace(void);
          111 
          112 /*
          113  * return date file was last modified
          114  */
          115 #define getdate prgetdate
          116 
          117 char*
          118 getdate(void)
          119 {
          120         static char *now = 0;
          121         static Dir *sbuf;
          122         ulong mtime;
          123 
          124         if(Nfiles > 1 || Files->f_name == nulls) {
          125                 if(now == 0) {
          126                         mtime = time(0);
          127                         now = ctime(mtime);
          128                 }
          129                 return now;
          130         }
          131         mtime = 0;
          132         sbuf = dirstat(Files->f_name);
          133         if(sbuf){
          134                 mtime = sbuf->mtime;
          135                 free(sbuf);
          136         }
          137         return ctime(mtime);
          138 }
          139 
          140 char*
          141 ffiler(char *s)
          142 {
          143         return smprint("can't open %s\n", s);
          144 }
          145 
          146 void
          147 main(int argc, char *argv[])
          148 {
          149         Fils fstr[NFILES];
          150         int nfdone = 0;
          151 
          152         Binit(&bout, 1, OWRITE);
          153         Files = fstr;
          154         for(argc = findopt(argc, argv); argc > 0; --argc, ++argv)
          155                 if(Multi == 'm') {
          156                         if(Nfiles >= NFILES - 1)
          157                                 die("too many files");
          158                         if(mustopen(*argv, &Files[Nfiles++]) == 0)
          159                                 nfdone++; /* suppress printing */
          160                 } else {
          161                         if(pr(*argv))
          162                                 Bterm(Files->f_f);
          163                         nfdone++;
          164                 }
          165         if(!nfdone)                        /* no files named, use stdin */
          166                 pr(nulls);                /* on GCOS, use current file, if any */
          167         errprint();                        /* print accumulated error reports */
          168         exits(error? "error": 0);
          169 }
          170 
          171 int
          172 findopt(int argc, char *argv[])
          173 {
          174         char **eargv = argv;
          175         int eargc = 0, c;
          176 
          177         while(--argc > 0) {
          178                 switch(c = **++argv) {
          179                 case '-':
          180                         if((c = *++*argv) == '\0')
          181                                 break;
          182                 case '+':
          183                         do {
          184                                 if(isdigit(c)) {
          185                                         --*argv;
          186                                         Ncols = atoix(argv);
          187                                 } else
          188                                 switch(c = TOLOWER(c)) {
          189                                 case '+':
          190                                         if((Fpage = atoix(argv)) < 1)
          191                                                 Fpage = 1;
          192                                         continue;
          193                                 case 'd':
          194                                         Dblspace = 2;
          195                                         continue;
          196                                 case 'e':
          197                                         TABS(Etabn, Etabc);
          198                                         continue;
          199                                 case 'f':
          200                                         Formfeed++;
          201                                         continue;
          202                                 case 'h':
          203                                         if(--argc > 0)
          204                                                 Head = argv[1];
          205                                         continue;
          206                                 case 'i':
          207                                         TABS(Itabn, Itabc);
          208                                         continue;
          209                                 case 'l':
          210                                         Len = atoix(argv);
          211                                         continue;
          212                                 case 'a':
          213                                 case 'm':
          214                                         Multi = c;
          215                                         continue;
          216                                 case 'o':
          217                                         Offset = atoix(argv);
          218                                         continue;
          219                                 case 's':
          220                                         if((Sepc = (*argv)[1]) != '\0')
          221                                                 ++*argv;
          222                                         else
          223                                                 Sepc = '\t';
          224                                         continue;
          225                                 case 't':
          226                                         Margin = 0;
          227                                         continue;
          228                                 case 'w':
          229                                         Linew = atoix(argv);
          230                                         continue;
          231                                 case 'n':
          232                                         Lnumb++;
          233                                         if((Numw = intopt(argv, &Nsepc)) <= 0)
          234                                                 Numw = NUMW;
          235                                 case 'b':
          236                                         Balance = 1;
          237                                         continue;
          238                                 case 'p':
          239                                         Padodd = 1;
          240                                         continue;
          241                                 default:
          242                                         die("bad option");
          243                                 }
          244                         } while((c = *++*argv) != '\0');
          245                         if(Head == argv[1])
          246                                 argv++;
          247                         continue;
          248                 }
          249                 *eargv++ = *argv;
          250                 eargc++;
          251         }
          252         if(Len == 0)
          253                 Len = LENGTH;
          254         if(Len <= Margin)
          255                 Margin = 0;
          256         Plength = Len - Margin/2;
          257         if(Multi == 'm')
          258                 Ncols = eargc;
          259         switch(Ncols) {
          260         case 0:
          261                 Ncols = 1;
          262         case 1:
          263                 break;
          264         default:
          265                 if(Etabn == 0)                /* respect explicit tab specification */
          266                         Etabn = DEFTAB;
          267         }
          268         if(Linew == 0)
          269                 Linew = Ncols != 1 && Sepc == 0? LINEW: 512;
          270         if(Lnumb)
          271                 Linew -= Multi == 'm'? Numw: Numw*Ncols;
          272         if((Colw = (Linew - Ncols + 1)/Ncols) < 1)
          273                 die("width too small");
          274         if(Ncols != 1 && Multi == 0) {
          275                 ulong buflen = ((ulong)(Plength/Dblspace + 1))*(Linew+1)*sizeof(char);
          276                 Buffer = getspace(buflen*sizeof(*Buffer));
          277                 Bufend = &Buffer[buflen];
          278                 Colpts = getspace((Ncols+1)*sizeof(*Colpts));
          279         }
          280         return eargc;
          281 }
          282 
          283 int
          284 intopt(char *argv[], int *optp)
          285 {
          286         int c;
          287 
          288         if((c = (*argv)[1]) != '\0' && !isdigit(c)) {
          289                 *optp = c;
          290                 (*argv)++;
          291         }
          292         c = atoix(argv);
          293         return c != 0? c: -1;
          294 }
          295 
          296 int
          297 pr(char *name)
          298 {
          299         char *date = 0, *head = 0;
          300 
          301         if(Multi != 'm' && mustopen(name, &Files[0]) == 0)
          302                 return 0;
          303         if(Buffer)
          304                 Bungetc(Files->f_f);
          305         if(Lnumb)
          306                 Lnumb = 1;
          307         for(Page = 0;; putpage()) {
          308                 if(C == -1)
          309                         break;
          310                 if(Buffer)
          311                         nexbuf();
          312                 Inpos = 0;
          313                 if(get(0) == -1)
          314                         break;
          315                 Bflush(&bout);
          316                 Page++;
          317                 if(Page >= Fpage) {
          318                         if(Margin == 0)
          319                                 continue;
          320                         if(date == 0)
          321                                 date = getdate();
          322                         if(head == 0)
          323                                 head = Head != 0 ? Head :
          324                                         Nfiles < 2? Files->f_name: nulls;
          325                         Bprint(&bout, "\n\n");
          326                         Nspace = Offset;
          327                         putspace();
          328                         Bprint(&bout, HEAD);
          329                 }
          330         }
          331         if(Padodd && (Page&1) == 1) {
          332                 Line = 0;
          333                 if(Formfeed)
          334                         put('\f');
          335                 else
          336                         while(Line < Len)
          337                                 put('\n');
          338         }
          339         C = '\0';
          340         return 1;
          341 }
          342 
          343 void
          344 putpage(void)
          345 {
          346         int colno;
          347 
          348         for(Line = Margin/2;; get(0)) {
          349                 for(Nspace = Offset, colno = 0, Outpos = 0; C != '\f';) {
          350                         if(Lnumb && C != -1 && (colno == 0 || Multi == 'a')) {
          351                                 if(Page >= Fpage) {
          352                                         putspace();
          353                                         Bprint(&bout, "%*ld", Numw, Buffer?
          354                                                 Colpts[colno].c_lno++: Lnumb);
          355                                         Outpos += Numw;
          356                                         put(Nsepc);
          357                                 }
          358                                 Lnumb++;
          359                         }
          360                         for(Lcolpos=0, Pcolpos=0; C!='\n' && C!='\f' && C!=-1; get(colno))
          361                                         put(C);
          362                         if(C==-1 || ++colno==Ncols || C=='\n' && get(colno)==-1)
          363                                 break;
          364                         if(Sepc)
          365                                 put(Sepc);
          366                         else
          367                                 if((Nspace += Colw - Lcolpos + 1) < 1)
          368                                         Nspace = 1;
          369                 }
          370         /*
          371                 if(C == -1) {
          372                         if(Margin != 0)
          373                                 break;
          374                         if(colno != 0)
          375                                 put('\n');
          376                         return;
          377                 }
          378         */
          379                 if(C == -1 && colno == 0) {
          380                         if(Margin != 0)
          381                                 break;
          382                         return;
          383                 }
          384                 if(C == '\f')
          385                         break;
          386                 put('\n');
          387                 if(Dblspace == 2 && Line < Plength)
          388                         put('\n');
          389                 if(Line >= Plength)
          390                         break;
          391         }
          392         if(Formfeed)
          393                 put('\f');
          394         else
          395                 while(Line < Len)
          396                         put('\n');
          397 }
          398 
          399 void
          400 nexbuf(void)
          401 {
          402         Rune *s = Buffer;
          403         Colp p = Colpts;
          404         int j, c, bline = 0;
          405 
          406         for(;;) {
          407                 p->c_ptr0 = p->c_ptr = s;
          408                 if(p == &Colpts[Ncols])
          409                         return;
          410                 (p++)->c_lno = Lnumb + bline;
          411                 for(j = (Len - Margin)/Dblspace; --j >= 0; bline++)
          412                         for(Inpos = 0;;) {
          413                                 if((c = Bgetrune(Files->f_f)) == -1) {
          414                                         for(*s = -1; p <= &Colpts[Ncols]; p++)
          415                                                 p->c_ptr0 = p->c_ptr = s;
          416                                         if(Balance)
          417                                                 balance(bline);
          418                                         return;
          419                                 }
          420                                 if(ISPRINT(c))
          421                                         Inpos++;
          422                                 if(Inpos <= Colw || c == '\n') {
          423                                         *s = c;
          424                                         if(++s >= Bufend)
          425                                                 die("page-buffer overflow");
          426                                 }
          427                                 if(c == '\n')
          428                                         break;
          429                                 switch(c) {
          430                                 case '\b':
          431                                         if(Inpos == 0)
          432                                                 s--;
          433                                 case ESC:
          434                                         if(Inpos > 0)
          435                                                 Inpos--;
          436                                 }
          437                         }
          438         }
          439 }
          440 
          441 /*
          442  * line balancing for last page
          443  */
          444 void
          445 balance(int bline)
          446 {
          447         Rune *s = Buffer;
          448         Colp p = Colpts;
          449         int colno = 0, j, c, l;
          450 
          451         c = bline % Ncols;
          452         l = (bline + Ncols - 1)/Ncols;
          453         bline = 0;
          454         do {
          455                 for(j = 0; j < l; ++j)
          456                         while(*s++ != '\n')
          457                                 ;
          458                 (++p)->c_lno = Lnumb + (bline += l);
          459                 p->c_ptr0 = p->c_ptr = s;
          460                 if(++colno == c)
          461                         l--;
          462         } while(colno < Ncols - 1);
          463 }
          464 
          465 int
          466 get(int colno)
          467 {
          468         static int peekc = 0;
          469         Colp p;
          470         Fils *q;
          471         long c;
          472 
          473         if(peekc) {
          474                 peekc = 0;
          475                 c = Etabc;
          476         } else
          477         if(Buffer) {
          478                 p = &Colpts[colno];
          479                 if(p->c_ptr >= (p+1)->c_ptr0)
          480                         c = -1;
          481                 else
          482                         if((c = *p->c_ptr) != -1)
          483                                 p->c_ptr++;
          484         } else
          485         if((c = (q = &Files[Multi == 'a'? 0: colno])->f_nextc) == -1) {
          486                 for(q = &Files[Nfiles]; --q >= Files && q->f_nextc == -1;)
          487                         ;
          488                 if(q >= Files)
          489                         c = '\n';
          490         } else
          491                 q->f_nextc = Bgetrune(q->f_f);
          492         if(Etabn != 0 && c == Etabc) {
          493                 Inpos++;
          494                 peekc = ETABS;
          495                 c = ' ';
          496         } else
          497         if(ISPRINT(c))
          498                 Inpos++;
          499         else
          500                 switch(c) {
          501                 case '\b':
          502                 case ESC:
          503                         if(Inpos > 0)
          504                                 Inpos--;
          505                         break;
          506                 case '\f':
          507                         if(Ncols == 1)
          508                                 break;
          509                         c = '\n';
          510                 case '\n':
          511                 case '\r':
          512                         Inpos = 0;
          513                 }
          514         return C = c;
          515 }
          516 
          517 void
          518 put(long c)
          519 {
          520         int move;
          521 
          522         switch(c) {
          523         case ' ':
          524                 Nspace++;
          525                 Lcolpos++;
          526                 return;
          527         case '\b':
          528                 if(Lcolpos == 0)
          529                         return;
          530                 if(Nspace > 0) {
          531                         Nspace--;
          532                         Lcolpos--;
          533                         return;
          534                 }
          535                 if(Lcolpos > Pcolpos) {
          536                         Lcolpos--;
          537                         return;
          538                 }
          539         case ESC:
          540                 move = -1;
          541                 break;
          542         case '\n':
          543                 Line++;
          544         case '\r':
          545         case '\f':
          546                 Pcolpos = 0;
          547                 Lcolpos = 0;
          548                 Nspace = 0;
          549                 Outpos = 0;
          550         default:
          551                 move = (ISPRINT(c) != 0);
          552         }
          553         if(Page < Fpage)
          554                 return;
          555         if(Lcolpos > 0 || move > 0)
          556                 Lcolpos += move;
          557         if(Lcolpos <= Colw) {
          558                 putspace();
          559                 Bputrune(&bout, c);
          560                 Pcolpos = Lcolpos;
          561                 Outpos += move;
          562         }
          563 }
          564 
          565 void
          566 putspace(void)
          567 {
          568         int nc;
          569 
          570         for(; Nspace > 0; Outpos += nc, Nspace -= nc)
          571                 if(ITABS)
          572                         Bputc(&bout, Itabc);
          573                 else {
          574                         nc = 1;
          575                         Bputc(&bout, ' ');
          576                 }
          577 }
          578 
          579 int
          580 atoix(char **p)
          581 {
          582         int n = 0, c;
          583 
          584         while(isdigit(c = *++*p))
          585                 n = 10*n + c - '0';
          586         (*p)--;
          587         return n;
          588 }
          589 
          590 /*
          591  * Defer message about failure to open file to prevent messing up
          592  * alignment of page with tear perforations or form markers.
          593  * Treat empty file as special case and report as diagnostic.
          594  */
          595 Biobuf*
          596 mustopen(char *s, Fils *f)
          597 {
          598         char *tmp;
          599 
          600         if(*s == '\0') {
          601                 f->f_name = STDINNAME();
          602                 f->f_f = malloc(sizeof(Biobuf));
          603                 if(f->f_f == 0)
          604                         cerror("no memory");
          605                 Binit(f->f_f, 0, OREAD);
          606         } else
          607         if((f->f_f = Bopen(f->f_name = s, OREAD)) == 0) {
          608                 tmp = ffiler(f->f_name);
          609                 s = strcpy((char*)getspace(strlen(tmp) + 1), tmp);
          610                 free(tmp);
          611         }
          612         if(f->f_f != 0) {
          613                 if((f->f_nextc = Bgetrune(f->f_f)) >= 0 || Multi == 'm')
          614                         return f->f_f;
          615                 sprint(s = (char*)getspace(strlen(f->f_name) + 1 + EMPTY),
          616                         "%s -- empty file\n", f->f_name);
          617                 Bterm(f->f_f);
          618         }
          619         error = 1;
          620         cerror(s);
          621         fprint(2, "\n");
          622         return 0;
          623 }
          624 
          625 void*
          626 getspace(ulong n)
          627 {
          628         void *t;
          629 
          630         if((t = malloc(n)) == 0)
          631                 die("out of space");
          632         return t;
          633 }
          634 
          635 void
          636 die(char *s)
          637 {
          638         error++;
          639         errprint();
          640         cerror(s);
          641         Bputc(&bout, '\n');
          642         exits("error");
          643 }
          644 
          645 /*
          646 void
          647 onintr(void)
          648 {
          649         error++;
          650         errprint();
          651         exits("error");
          652 }
          653 /**/
          654 
          655 /*
          656  * print accumulated error reports
          657  */
          658 void
          659 errprint(void)
          660 {
          661         Bflush(&bout);
          662         for(; err != 0; err = err->e_nextp) {
          663                 cerror(err->e_mess);
          664                 fprint(2, "\n");
          665         }
          666 }