1 | /* fmt.c */
|
---|
2 |
|
---|
3 | /* usage: fmt [-width] [files]...
|
---|
4 | *
|
---|
5 | * Fmt rearrages text in order to make each line have roughly the
|
---|
6 | * same width. Indentation and word spacing is preserved.
|
---|
7 | *
|
---|
8 | * The default width is 72 characters, but you can override that via -width.
|
---|
9 | * If no files are given on the command line, then it reads stdin.
|
---|
10 | */
|
---|
11 |
|
---|
12 | #include <stdio.h>
|
---|
13 |
|
---|
14 | #ifndef TRUE
|
---|
15 | # define TRUE 1
|
---|
16 | # define FALSE 0
|
---|
17 | #endif
|
---|
18 |
|
---|
19 |
|
---|
20 |
|
---|
21 | int width = 72; /* the desired line width */
|
---|
22 | int isblank; /* is the current output line blank? */
|
---|
23 | int indent; /* width of the indentation */
|
---|
24 | char ind[512]; /* indentation text */
|
---|
25 | char word[1024]; /* word buffer */
|
---|
26 |
|
---|
27 | /* This function displays a usage message and quits */
|
---|
28 | void usage()
|
---|
29 | {
|
---|
30 | fprintf(stderr, "usage: fmt [-width] [files]...\n");
|
---|
31 | exit(2);
|
---|
32 | }
|
---|
33 |
|
---|
34 |
|
---|
35 |
|
---|
36 | /* This function outputs a single word. It takes care of spacing and the
|
---|
37 | * newlines within a paragraph.
|
---|
38 | */
|
---|
39 | void putword()
|
---|
40 | {
|
---|
41 | int i; /* index into word[], or whatever */
|
---|
42 | int ww; /* width of the word */
|
---|
43 | int sw; /* width of spacing after word */
|
---|
44 | static int psw; /* space width of previous word */
|
---|
45 | static int tab; /* the width of text already written */
|
---|
46 |
|
---|
47 |
|
---|
48 | /* separate the word and its spacing */
|
---|
49 | for (ww = 0; word[ww] && word[ww] != ' '; ww++)
|
---|
50 | {
|
---|
51 | }
|
---|
52 | sw = strlen(word) - ww;
|
---|
53 | word[ww] = '\0';
|
---|
54 |
|
---|
55 | /* if no spacing (that is, the word was at the end of the line) then
|
---|
56 | * assume 1 space unless the last char of the word was punctuation
|
---|
57 | */
|
---|
58 | if (sw == 0)
|
---|
59 | {
|
---|
60 | sw = 1;
|
---|
61 | if (word[ww - 1] == '.' || word[ww - 1] == '?' || word[ww - 1] == '!')
|
---|
62 | sw = 2;
|
---|
63 | }
|
---|
64 |
|
---|
65 | /* if this is the first word on the line... */
|
---|
66 | if (isblank)
|
---|
67 | {
|
---|
68 | /* output the indentation first */
|
---|
69 | fputs(ind, stdout);
|
---|
70 | tab = indent;
|
---|
71 | }
|
---|
72 | else /* text has already been written to this output line */
|
---|
73 | {
|
---|
74 | /* will the word fit on this line? */
|
---|
75 | if (psw + ww + tab <= width)
|
---|
76 | {
|
---|
77 | /* yes - so write the previous word's spacing */
|
---|
78 | for (i = 0; i < psw; i++)
|
---|
79 | {
|
---|
80 | putchar(' ');
|
---|
81 | }
|
---|
82 | tab += psw;
|
---|
83 | }
|
---|
84 | else
|
---|
85 | {
|
---|
86 | /* no, so write a newline and the indentation */
|
---|
87 | putchar('\n');
|
---|
88 | fputs(ind, stdout);
|
---|
89 | tab = indent;
|
---|
90 | }
|
---|
91 | }
|
---|
92 |
|
---|
93 | /* write the word itself */
|
---|
94 | fputs(word, stdout);
|
---|
95 | tab += ww;
|
---|
96 |
|
---|
97 | /* remember this word's spacing */
|
---|
98 | psw = sw;
|
---|
99 |
|
---|
100 | /* this output line isn't blank anymore. */
|
---|
101 | isblank = FALSE;
|
---|
102 | }
|
---|
103 |
|
---|
104 |
|
---|
105 |
|
---|
106 | /* This function reformats text. */
|
---|
107 | void fmt(in)
|
---|
108 | FILE *in; /* the name of the input stream */
|
---|
109 | {
|
---|
110 | int ch; /* character from input stream */
|
---|
111 | int prevch; /* the previous character in the loop */
|
---|
112 | int i; /* index into ind[] or word[] */
|
---|
113 | int inword; /* boolean: are we between indent & newline? */
|
---|
114 |
|
---|
115 |
|
---|
116 | /* for each character in the stream... */
|
---|
117 | for (indent = -1, isblank = TRUE, inword = FALSE, i = 0, prevch = '\n';
|
---|
118 | (ch = getc(in)) != EOF;
|
---|
119 | prevch = ch)
|
---|
120 | {
|
---|
121 | /* is this the end of a line? */
|
---|
122 | if (ch == '\n')
|
---|
123 | {
|
---|
124 | /* if end of last word in the input line */
|
---|
125 | if (inword)
|
---|
126 | {
|
---|
127 | /* if it really is a word */
|
---|
128 | if (i > 0)
|
---|
129 | {
|
---|
130 | /* output it */
|
---|
131 | word[i] = '\0';
|
---|
132 | putword();
|
---|
133 | }
|
---|
134 | }
|
---|
135 | else /* blank line in input */
|
---|
136 | {
|
---|
137 | /* finish the previous paragraph */
|
---|
138 | if (!isblank)
|
---|
139 | {
|
---|
140 | putchar('\n');
|
---|
141 | isblank = TRUE;
|
---|
142 | }
|
---|
143 |
|
---|
144 | /* output a blank line */
|
---|
145 | putchar('\n');
|
---|
146 | }
|
---|
147 |
|
---|
148 | /* continue with next input line... */
|
---|
149 | indent = -1;
|
---|
150 | i = 0;
|
---|
151 | inword = FALSE;
|
---|
152 | continue;
|
---|
153 | }
|
---|
154 |
|
---|
155 | /* if we're expecting indentation now... */
|
---|
156 | if (indent < 0)
|
---|
157 | {
|
---|
158 | /* if this is part of the indentation... */
|
---|
159 | if (ch == ' ' || ch == '\t')
|
---|
160 | {
|
---|
161 | /* remember it */
|
---|
162 | ind[i++] = ch;
|
---|
163 | }
|
---|
164 | else /* end of indentation */
|
---|
165 | {
|
---|
166 | /* mark the end of the indentation string */
|
---|
167 | ind[i] = '\0';
|
---|
168 |
|
---|
169 | /* calculate the width of the indentation */
|
---|
170 | for (i = indent = 0; ind[i]; i++)
|
---|
171 | {
|
---|
172 | if (ind[i] == '\t')
|
---|
173 | indent = (indent | 7) + 1;
|
---|
174 | else
|
---|
175 | indent++;
|
---|
176 | }
|
---|
177 |
|
---|
178 | /* reset the word index */
|
---|
179 | i = 0;
|
---|
180 |
|
---|
181 | /* reprocess that last character */
|
---|
182 | ungetc(ch, in);
|
---|
183 | }
|
---|
184 |
|
---|
185 | /* continue in the for-loop */
|
---|
186 | continue;
|
---|
187 | }
|
---|
188 |
|
---|
189 | /* if we get here, we're either in a word or in the space
|
---|
190 | * after a word.
|
---|
191 | */
|
---|
192 | inword = TRUE;
|
---|
193 |
|
---|
194 | /* is this the start of a new word? */
|
---|
195 | if (ch != ' ' && prevch == ' ')
|
---|
196 | {
|
---|
197 | /* yes! output the previous word */
|
---|
198 | word[i] = '\0';
|
---|
199 | putword();
|
---|
200 |
|
---|
201 | /* reset `i' to the start of the word[] buffer */
|
---|
202 | i = 0;
|
---|
203 | }
|
---|
204 | word[i++] = ch;
|
---|
205 | }
|
---|
206 |
|
---|
207 | /* if necessary, write a final newline */
|
---|
208 | if (!isblank)
|
---|
209 | {
|
---|
210 | putchar('\n');
|
---|
211 | isblank = TRUE;
|
---|
212 | }
|
---|
213 | }
|
---|
214 |
|
---|
215 |
|
---|
216 |
|
---|
217 |
|
---|
218 |
|
---|
219 | int main(argc, argv)
|
---|
220 | int argc;
|
---|
221 | char **argv;
|
---|
222 | {
|
---|
223 | FILE *in; /* an input stream */
|
---|
224 | int error; /* if non-zero, then an error occurred */
|
---|
225 | int i;
|
---|
226 |
|
---|
227 |
|
---|
228 | /* handle the -width flag, if given */
|
---|
229 | if (argc > 1 && argv[1][0] == '-')
|
---|
230 | {
|
---|
231 | width = atoi(argv[1] + 1);
|
---|
232 | if (width <= 0)
|
---|
233 | {
|
---|
234 | usage();
|
---|
235 | }
|
---|
236 | argc--;
|
---|
237 | argv++;
|
---|
238 | }
|
---|
239 |
|
---|
240 | /* if no filenames given, then process stdin */
|
---|
241 | if (argc == 1)
|
---|
242 | {
|
---|
243 | fmt(stdin);
|
---|
244 | }
|
---|
245 | else /* one or more filenames given */
|
---|
246 | {
|
---|
247 | for (error = 0, i = 1; i < argc; i++)
|
---|
248 | {
|
---|
249 | in = fopen(argv[i], "r");
|
---|
250 | if (!in)
|
---|
251 | {
|
---|
252 | perror(argv[i]);
|
---|
253 | error = 3;
|
---|
254 | }
|
---|
255 | else
|
---|
256 | {
|
---|
257 | fmt(in);
|
---|
258 | fclose(in);
|
---|
259 | }
|
---|
260 | }
|
---|
261 | }
|
---|
262 |
|
---|
263 | /* exit, possibly indicating an error */
|
---|
264 | exit(error);
|
---|
265 | /*NOTREACHED*/
|
---|
266 | }
|
---|