20 static const char *ego = "<unset>";
22 static const char *bkp = 0;
24 static unsigned flags = 0;
25 #define F_MIDLINETABS 1u
29 #define F_UNTABIFY 16u
34 static void usage(FILE *fp)
35 { fprintf(fp, "Usage: %s [-clmtuv] [-i[BKP]] [FILE...]\n\n", ego); }
37 static char *augment(const char *name, const char *suffix)
39 size_t n = strlen(name), nn = strlen(suffix);
40 char *p = malloc(n + nn + 1);
43 fprintf(stderr, "%s: Out of memory!\n", ego);
47 memcpy(p + n, suffix, nn + 1);
51 static FILE *freshname(const char *name, char **newname, mode_t mode)
59 for (i = 0; i < 32767; i++) {
60 sprintf(buf, ".new%d", i);
61 if ((n = augment(name, buf)) == 0)
63 if ((fd = open(n, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0) {
64 if (errno == EEXIST) {
68 fprintf(stderr, "%s: Can't create new file for `%s': %s\n",
69 ego, name, strerror(errno));
74 fprintf(stderr, "%s: Can't find new file to update `%s'\n", ego, name);
79 fprintf(stderr, "%s: Can't set permissions on `%s': %s\n",
80 ego, n, strerror(errno));
83 if ((fp = fdopen(fd, "w")) == 0) {
84 fprintf(stderr, "%s: fdopen on `%s' failed: %s\n",
85 ego, n, strerror(errno));
104 #define BUF_INIT { 0, 0, 0 }
106 static void reset(struct buf *b) { b->n = 0; }
108 static int put(struct buf *b, int ch)
118 b->b = realloc(b->b, w);
121 fprintf(stderr, "%s: Not enough memory for buffer!\n", ego);
130 #define TABSTOP(n) (((n) + 8u) & ~7u)
132 static int space(const char *name)
134 static struct buf b = BUF_INIT;
135 FILE *fin, *fout = stdout;
136 char *newname = 0, *oldname = 0;
137 int rc = TROUBLE, status = OK;
139 unsigned nsp = 0, nwsp = 0, nnl = 0;
140 unsigned hpos = 0, ohpos = 0, nhpos = 0, nl = 1;
143 #define f_warnspacetab 2u
145 #define f_warntabs 8u
146 #define f_warnspaces 16u
149 #define f_forced 128u
151 unsigned f = f_newline | f_begin | (flags & F_TABIFY ? f_tabify : 0);
154 if (strcmp(name, "-") == 0) {
155 if (flags & F_INPLACE) {
156 fprintf(stderr, "%s: Can't modify stdin in-place.\n", ego);
161 if ((fin = fopen(name, "r")) == 0) {
162 fprintf(stderr, "%s: Failed to open file `%s': %s.\n",
163 ego, name, strerror(errno));
166 else if (flags & F_INPLACE) {
168 if (stat(name, &st)) {
169 fprintf(stderr, "%s: Can't stat `%s': %s.\n",
170 ego, name, strerror(errno));
173 if ((fout = freshname(name, &newname, st.st_mode)) == 0)
184 nsp++; nwsp++; hpos++;
185 if (put(&b, ' ')) goto done_2;
188 if (flags & F_UNTABIFY) {
189 if ((flags & F_VERBOSE) && !(f & f_warntabs)) {
190 fprintf(stderr, "%s:%u: found tab\n", name, nl);
194 } else if (((flags & F_MIDLINETABS) || (f & f_newline)) && nsp) {
195 if ((flags & F_VERBOSE) && !(f & f_warnspacetab)) {
196 fprintf(stderr, "%s:%u: space followed by tab\n", name, nl);
200 f |= f_tabify | f_forced;
203 nsp = 0; nwsp++; hpos = TABSTOP(hpos);
204 if (put(&b, '\t')) goto done_2;
207 if (!(f & f_begin)) {
208 if (!(f & f_newline)) {
209 if (flags & F_VERBOSE)
210 fprintf(stderr, "%s:%u: file ends in mid-line\n", name, nl);
213 } else if (nnl > 1) {
214 if (flags & F_TBLANK) {
215 if (flags & F_VERBOSE) {
216 fprintf(stderr, "%s:%u: file has trailing blank lines\n",
223 if (fout) while (nnl--) putc('\n', fout);
229 if (flags & F_VERBOSE)
230 fprintf(stderr, "%s:%u: trailing whitespace\n", name, nl);
234 nsp = nwsp = hpos = ohpos = 0; nl++;
238 while (nnl) { putc('\n', fout); nnl--; }
242 f &= ~(f_tab | f_warnspacetab | f_warntabs | f_warnspaces);
243 if (flags & F_TABIFY)
250 if (fout) while (nnl) { putc('\n', fout); nnl--; }
252 if (flags & F_UNTABIFY) {
253 if (fout) for (; ohpos < hpos; ohpos++) putc(' ', fout);
254 } else if ((f & f_tabify) &&
255 ((hpos - ohpos >= (last == '.' || last == ':' ?
257 (f & (f_tab | f_newline)))) {
260 nhpos = TABSTOP(ohpos);
261 if (nhpos > hpos) break;
262 if (fout) putc('\t', fout);
263 if ((flags & F_VERBOSE) && (flags & F_TABIFY) &&
264 i < b.n && b.b[i] != '\t' &&
265 !(f & (f_warnspaces | f_forced))) {
266 fprintf(stderr, "%s:%u: spaces could be turned into tabs\n",
274 for (; ohpos < hpos; ohpos++) putc(' ', fout);
276 for (i = 0; i < b.n; i++) putc(b.b[i], fout);
279 f &= ~(f_newline | f_tab | f_forced);
280 if (!(flags & F_TABIFY) || !(flags & F_MIDLINETABS)) f &= ~f_tabify;
282 hpos++; ohpos = hpos;
283 if (fout) putc(ch, fout);
284 if (ch != '"' && ch != '\'')
293 fprintf(stderr, "%s: Error reading `%s': %s\n",
294 ego, name, strerror(errno));
299 if (fflush(fout) || ferror(fout)) f |= f_bad;
300 if (fout != stdout && fclose(fout)) f |= f_bad;
303 fprintf(stderr, "%s: Error writing `%s': %s\n",
304 ego, newname, strerror(errno));
309 if (flags & F_INPLACE) {
311 if ((oldname = augment(name, bkp)) == 0)
313 if (rename(name, oldname)) {
314 fprintf(stderr, "%s: Failed to back up `%s' as `%s': %s\n",
315 ego, name, oldname, strerror(errno));
319 if (rename(newname, name)) {
320 if (oldname) rename(oldname, name);
321 fprintf(stderr, "%s: Failed to install `%s' as `%s': %s\n",
322 ego, newname, name, strerror(errno));
330 if (oldname) free(oldname);
336 if (fout && fout != stdout) fclose(fout);
342 static int manysetp(unsigned f) { return (!!(f & (f - 1))); }
344 int main(int argc, char *argv[])
349 if ((ego = strrchr(argv[0], '/')) == 0)
355 if ((i = getopt(argc, argv, "h" "clmtuv" "i::")) < 0)
359 printf("%s -- remove extraneous spaces from files\n\n", ego);
362 -h Print this help text\n\
363 -c Check files for badness, but don't produce other output\n\
364 -l Check for, and/or remove, trailing blank lines\n\
365 -m Fix spaces followed by tabs in mid-line\n\
366 -t Tabify file completely\n\
367 -u Untabify file completely\n\
368 -v Report verbose messages\n\
369 -i[BKP] Modify files in place; leave FILEBKP as copy of old FILE\n\
377 flags |= F_MIDLINETABS;
399 if (flags & F_BOGUS) {
403 if (manysetp(flags & (F_CHECK | F_INPLACE))) {
404 fprintf(stderr, "%s: Options -c and -i are mutually exclusive.\n", ego);
407 if (manysetp(flags & (F_TABIFY | F_UNTABIFY))) {
408 fprintf(stderr, "%s: Options -t and -u are mutually exclusive.\n", ego);
412 if (optind == argc) {
414 fprintf(stderr, "%s: No options given and stdin is a terminal.\n",
419 } else for (i = optind; i < argc; i++) {
421 if (st > rc) rc = st;
423 if (rc == BADNESS && !(flags & F_CHECK))