URI: 
       fmt.c - 9base - revived minimalist port of Plan 9 userland to Unix
  HTML git clone git://git.suckless.org/9base
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       fmt.c (3745B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <bio.h>
            4 #include <ctype.h>
            5 
            6 /*
            7  * block up paragraphs, possibly with indentation
            8  */
            9 
           10 int extraindent = 0;                /* how many spaces to indent all lines */
           11 int indent = 0;                        /* current value of indent, before extra indent */
           12 int length = 70;                /* how many columns per output line */
           13 int join = 1;                        /* can lines be joined? */
           14 int maxtab = 8;
           15 
           16 Biobuf bin;
           17 Biobuf bout;
           18 
           19 typedef struct Word Word;
           20 struct Word
           21 {
           22         Word        *next;
           23 
           24         int        indent;
           25         int        length;
           26         char        bol;
           27         char        text[];
           28 };
           29 
           30 void        fmt(void);
           31 
           32 void
           33 usage(void)
           34 {
           35         fprint(2, "usage: %s [-j] [-i indent] [-l length] [file...]\n", argv0);
           36         exits("usage");
           37 }
           38 
           39 void
           40 main(int argc, char **argv)
           41 {
           42         int i, f;
           43         char *s, *err;
           44 
           45         ARGBEGIN{
           46         case 'i':
           47                 extraindent = atoi(EARGF(usage()));
           48                 break;
           49         case 'j':
           50                 join = 0;
           51                 break;
           52         case 'w':
           53         case 'l':
           54                 length = atoi(EARGF(usage()));
           55                 break;
           56         default:
           57                 usage();
           58         }ARGEND
           59 
           60         if(length <= indent){
           61                 fprint(2, "%s: line length<=indentation\n", argv0);
           62                 exits("length");
           63         }
           64 
           65         s=getenv("tabstop");
           66         if(s!=nil && atoi(s)>0)
           67                 maxtab=atoi(s);
           68         err = nil;
           69         Binit(&bout, 1, OWRITE);
           70         if(argc <= 0){
           71                 Binit(&bin, 0, OREAD);
           72                 fmt();
           73         }else{
           74                 for(i=0; i<argc; i++){
           75                         f = open(argv[i], OREAD);
           76                         if(f < 0){
           77                                 fprint(2, "%s: can't open %s: %r\n", argv0, argv[i]);
           78                                 err = "open";
           79                         }else{
           80                                 Binit(&bin, f, OREAD);
           81                                 fmt();
           82                                 Bterm(&bin);
           83                                 if(i != argc-1)
           84                                         Bputc(&bout, '\n');
           85                         }
           86                 }
           87         }
           88         exits(err);
           89 }
           90 
           91 int
           92 indentof(char *s)
           93 {
           94         int ind;
           95 
           96         ind = 0;
           97         for(; *s != '\0'; s++)
           98                 switch(*s){
           99                 default:
          100                         return ind;
          101                 case ' ':
          102                         ind++;
          103                         break;
          104                 case '\t':
          105                         ind += maxtab;
          106                         ind -= ind%maxtab;
          107                         break;
          108                 }
          109 
          110         /* plain white space doesn't change the indent */
          111         return indent;
          112 }
          113 
          114 Word*
          115 newword(char *s, int n, int ind, int bol)
          116 {
          117         Word *w;
          118 
          119         w = malloc(sizeof(Word) + n+1);
          120         w->next = nil;
          121         w->indent = ind;
          122         w->bol = bol;
          123         memmove(w->text, s, n);
          124         w->text[n] = 0;
          125         w->length = utflen(w->text);
          126         return w;
          127 }
          128 
          129 Word*
          130 getword(void)
          131 {
          132         static Word *head, *tail;
          133         char *line, *s;
          134         Word *w;
          135         
          136         w = head;
          137         if(w != nil){
          138                 head = w->next;
          139                 return w;
          140         }
          141         line = Brdstr(&bin, '\n', 1);
          142         if(line == nil)
          143                 return nil;
          144         tail = nil;
          145         indent = indentof(line);
          146         for(;;){
          147                 while(*line == ' ' || *line == '\t')
          148                         line++;
          149                 if(*line == '\0'){
          150                         if(head == nil)
          151                                 return newword("", 0, -1, 1);
          152                         break;
          153                 }
          154                 /* how long is this word? */
          155                 for(s=line++; *line != '\0'; line++)
          156                         if(*line==' ' || *line=='\t')
          157                                 break;
          158                 w = newword(s, line-s, indent, head==nil);
          159                 if(head == nil)
          160                         head = w;
          161                 else
          162                         tail->next = w;
          163                 tail = w;
          164         }
          165         w = head;
          166         head = w->next;
          167         return w;
          168 }
          169 
          170 void
          171 printindent(int w)
          172 {
          173         while(w >= maxtab){
          174                 Bputc(&bout, '\t');
          175                 w -= maxtab;
          176         }
          177         while(w > 0){
          178                 Bputc(&bout, ' ');
          179                 w--;
          180         }
          181 }
          182 
          183 /* give extra space if word ends with period, etc. */
          184 int
          185 nspaceafter(char *s)
          186 {
          187         int n;
          188 
          189         n = strlen(s);
          190         if(n < 2)
          191                 return 1;
          192         if(isupper(s[0]) && n < 4)
          193                 return 1;
          194         if(strchr(".!?", s[n-1]) != nil)
          195                 return 2;
          196         return 1;
          197 }
          198 
          199 void
          200 fmt(void)
          201 {
          202         Word *w, *o;
          203         int col, nsp;
          204 
          205         w = getword();
          206         while(w != nil){
          207                 if(w->indent == -1){
          208                         Bputc(&bout, '\n');
          209                         free(w);
          210                         w = getword();
          211                         if(w == nil)
          212                                 break;
          213                 }
          214                 col = w->indent;
          215                 printindent(extraindent+col);
          216                 /* emit words until overflow; always emit at least one word */
          217                 for(;;){
          218                         Bprint(&bout, "%s", w->text);
          219                         col += w->length;
          220                         o = w;
          221                         w = getword();
          222                         if(w == nil)
          223                                 break;
          224                         if(w->indent != o->indent)
          225                                 break;        /* indent change */
          226                         nsp = nspaceafter(o->text);
          227                         if(col+nsp+w->length > length)
          228                                 break;        /* fold line */
          229                         if(!join && w->bol)
          230                                 break;
          231                         while(--nsp >= 0){
          232                                 Bputc(&bout, ' ');        /* emit space; another word will follow */
          233                                 col++;
          234                         }
          235                         free(o);
          236                 }
          237                 free(o);
          238                 Bputc(&bout, '\n');
          239         }
          240 }