xref: /plan9-contrib/sys/src/cmd/fmt.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
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 void	fmt(Biobuf*);
11 void	outchar(Rune);
12 void	addrune(Rune);
13 void	outword(void);
14 void	puncttest(void);
15 void	flush(void);
16 
17 int join=1;		/* can input lines be joined? */
18 int indent=0;		/* how many spaces to indent */
19 int length=70;		/* how many columns per output line */
20 Biobuf bin;
21 Biobuf bout;
22 
23 void
24 main(int argc, char **argv)
25 {
26 	int i, f;
27 	ARGBEGIN{
28 	case 'i':
29 		indent = atoi(ARGF());
30 		break;
31 	case 'j':
32 		join = 0;
33 		break;
34 	case 'w':
35 	case 'l':
36 		length = atoi(ARGF());
37 		break;
38 	default:
39 		fprint(2, "Usage: %s [-j] [-i indent] [-l length] [file ...]\n",
40 				argv0);
41 		exits("usage");
42 	}ARGEND
43 	if(length<=indent){
44 		fprint(2, "%s: line length<=indentation\n", argv0);
45 		exits("length");
46 	}
47 	Binit(&bout, 1, OWRITE);
48 	if(argc<=0){
49 		Binit(&bin, 0, OREAD);
50 		fmt(&bin);
51 	}else{
52 		for(i=0; i<argc; i++){
53 			f=open(argv[i], OREAD);
54 			if(f<0)
55 				perror(argv[i]);
56 			else{
57 				Binit(&bin, f, OREAD);
58 				fmt(&bin);
59 				Bterm(&bin);
60 				if(i!=argc-1)
61 					Bputc(&bout, '\n');
62 			}
63 		}
64 	}
65 	exits(0);
66 }
67 void
68 fmt(Biobuf *f)
69 {
70 	long c;
71 
72 	while((c = Bgetrune(f)) >= 0)
73 		outchar((Rune) c);
74 	flush();
75 }
76 
77 #define TAB 8
78 #define	NWORD	(TAB*32)
79 
80 Rune word[NWORD+1];
81 Rune *wp=word;
82 int col=0;	/* output column number */
83 int bol=1;	/* at beginning of output line? */
84 int punct=0;	/* last character out was punctuation? */
85 int newline=1;	/* last char read was newline(1) or init space(2) */
86 
87 void
88 outchar(Rune c){
89 	switch(c){
90 	case '\0':
91 		break;
92 	case '\n':
93 		switch(newline){
94 		case 0:
95 			if(join)
96 				outword();
97 			else
98 				flush();
99 			break;
100 		case 1:
101 			flush();
102 		case 2:
103 			Bputc(&bout, '\n');
104 			wp=word;
105 		}
106 		newline=1;
107 		break;
108 	case ' ':
109 	case '\t':
110 		switch(newline) {
111 		case 0:
112 			outword();
113 			break;
114 		case 1:
115 			flush();
116 			newline=2;
117 		case 2:
118 			do {
119 				addrune(L' ');
120 			} while(c=='\t' && (wp-word)%TAB);
121 		}
122 		break;
123 	default:
124 		addrune(c);
125 		newline=0;
126 	}
127 }
128 
129 void
130 addrune(Rune c)
131 {
132 	if(wp==&word[NWORD]) {
133 		if(utfrune(" \t",wp[-1]))
134 			wp=word;
135 		outword();
136 	}
137 	*wp++=c;
138 }
139 
140 void
141 outword(void)
142 {
143 	int i;
144 
145 	if(wp==word)
146 		return;
147 	if(wp-word+col+(bol?0:punct?2:1)>length){
148 		Bputc(&bout, '\n');
149 		col=0;
150 		bol=1;
151 	}
152 	if(col==0){
153 		for(i=0;i+8<=indent;i+=8)
154 			Bputc(&bout, '\t');
155 		while(i++<indent)
156 			Bputc(&bout, ' ');
157 		col=indent;
158 	}
159 	if(bol)
160 		bol=0;
161 	else{
162 		if(punct){
163 			Bputc(&bout, ' ');
164 			col++;
165 		}
166 		Bputc(&bout, ' ');
167 		col++;
168 	}
169 	puncttest();
170 	for (i = 0; word+i < wp; i++)
171 		Bputrune(&bout, word[i]);
172 	col+=i;
173 	wp=word;
174 }
175 /* is the word followed by major punctuation, .?:! */
176 /* disregard short things followed by periods; they are probably
177    initials or titles like Mrs. and Dr. */
178 void
179 puncttest(void)
180 {
181 	Rune *rp;
182 
183 	punct = 0;
184 	for(rp=wp; --rp>=word; ) {
185 		switch(*rp) {
186 		case ')': case '\'': case '"':
187 			continue;
188 		case '.':
189 			if(isupper(*word)&&rp-word<=3)
190 				return;
191 		case '?': case '!': /*case ':':*/
192 			punct = 1;
193 		default:
194 			return;
195 		}
196 	}
197 }
198 void
199 flush(void){
200 	outword();
201 	if(col!=0){
202 		Bputc(&bout, '\n');
203 		col=0;
204 		bol=1;
205 	}
206 }
207