]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/fmt.c
make bind(2) error handling consistent
[plan9front.git] / sys / src / cmd / fmt.c
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 }