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
); else nnl
= 0;
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
--; }
243 f
&= ~(f_tab
| f_warnspacetab
| f_warntabs
| f_warnspaces
);
244 if (flags
& F_TABIFY
)
251 if (fout
) while (nnl
) { putc('\n', fout
); nnl
--; } else nnl
= 0;
253 if (flags
& F_UNTABIFY
) {
254 if (fout
) for (; ohpos
< hpos
; ohpos
++) putc(' ', fout
);
255 } else if ((f
& f_tabify
) &&
256 ((hpos
- ohpos
>= (last
== '.' || last
== ':' ?
258 (f
& (f_tab
| f_newline
)))) {
261 nhpos
= TABSTOP(ohpos
);
262 if (nhpos
> hpos
) break;
263 if (fout
) putc('\t', fout
);
264 if ((flags
& F_VERBOSE
) && (flags
& F_TABIFY
) &&
265 i
< b
.n
&& b
.b
[i
] != '\t' &&
266 !(f
& (f_warnspaces
| f_forced
))) {
267 fprintf(stderr
, "%s:%u: spaces could be turned into tabs\n",
275 for (; ohpos
< hpos
; ohpos
++) putc(' ', fout
);
277 for (i
= 0; i
< b
.n
; i
++) putc(b
.b
[i
], fout
);
280 f
&= ~(f_newline
| f_tab
| f_forced
);
281 if (!(flags
& F_TABIFY
) || !(flags
& F_MIDLINETABS
)) f
&= ~f_tabify
;
283 hpos
++; ohpos
= hpos
;
284 if (fout
) putc(ch
, fout
);
285 if (ch
!= '"' && ch
!= '\'')
294 fprintf(stderr
, "%s: Error reading `%s': %s\n",
295 ego
, name
, strerror(errno
));
300 if (fflush(fout
) || ferror(fout
)) f
|= f_bad
;
301 if (fout
!= stdout
&& fclose(fout
)) f
|= f_bad
;
304 fprintf(stderr
, "%s: Error writing `%s': %s\n",
305 ego
, newname
, strerror(errno
));
310 if (flags
& F_INPLACE
) {
312 if ((oldname
= augment(name
, bkp
)) == 0)
314 if (rename(name
, oldname
)) {
315 fprintf(stderr
, "%s: Failed to back up `%s' as `%s': %s\n",
316 ego
, name
, oldname
, strerror(errno
));
320 if (rename(newname
, name
)) {
321 if (oldname
) rename(oldname
, name
);
322 fprintf(stderr
, "%s: Failed to install `%s' as `%s': %s\n",
323 ego
, newname
, name
, strerror(errno
));
331 if (oldname
) free(oldname
);
337 if (fout
&& fout
!= stdout
) fclose(fout
);
343 static int manysetp(unsigned f
) { return (!!(f
& (f
- 1))); }
345 int main(int argc
, char *argv
[])
350 if ((ego
= strrchr(argv
[0], '/')) == 0)
356 if ((i
= getopt(argc
, argv
, "h" "clmtuv" "i::")) < 0)
360 printf("%s -- remove extraneous spaces from files\n\n", ego
);
363 -h Print this help text\n\
364 -c Check files for badness, but don't produce other output\n\
365 -l Check for, and/or remove, trailing blank lines\n\
366 -m Fix spaces followed by tabs in mid-line\n\
367 -t Tabify file completely\n\
368 -u Untabify file completely\n\
369 -v Report verbose messages\n\
370 -i[BKP] Modify files in place; leave FILEBKP as copy of old FILE\n\
378 flags
|= F_MIDLINETABS
;
400 if (flags
& F_BOGUS
) {
404 if (manysetp(flags
& (F_CHECK
| F_INPLACE
))) {
405 fprintf(stderr
, "%s: Options -c and -i are mutually exclusive.\n", ego
);
408 if (manysetp(flags
& (F_TABIFY
| F_UNTABIFY
))) {
409 fprintf(stderr
, "%s: Options -t and -u are mutually exclusive.\n", ego
);
413 if (optind
== argc
) {
415 fprintf(stderr
, "%s: No options given and stdin is a terminal.\n",
420 } else for (i
= optind
; i
< argc
; i
++) {
422 if (st
> rc
) rc
= st
;
424 if (rc
== BADNESS
&& !(flags
& F_CHECK
))