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 }