1 /* fmt.c - Text formatter
2 *
3 * Copyright 2017 The Android Open Source Project
4 *
5 * No standard.
6 *
7 * Only counts space and tab for indent level (eats other low ascii chars,
8 * treats all UTF8 chars as non-whitespace), preserves indentation but squashes
9 * together runs of whitespace. No header/footer logic, no end-of-sentence
10 * double-space, preserves initial tab/space mix when indenting new lines.
11
12 USE_FMT(NEWTOY(fmt, "w#<0=75", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
13
14 config FMT
15 bool "fmt"
16 default y
17 help
18 usage: fmt [-w WIDTH] [FILE...]
19
20 Reformat input to wordwrap at a given line length, preserving existing
21 indentation level, writing to stdout.
22
23 -w WIDTH Maximum characters per line (default 75)
24 */
25
26 #define FOR_fmt
27 #include "toys.h"
28
GLOBALS(int width;int level,pos;)29 GLOBALS(
30 int width;
31
32 int level, pos;
33 )
34
35 static void newline(void)
36 {
37 if (TT.pos) xputc('\n');
38 TT.pos = 0;
39 }
40
41 // Process lines of input, with (0,0) flush between files
fmt_line(char ** pline,long len)42 static void fmt_line(char **pline, long len)
43 {
44 char *line;
45 int idx, indent, count;
46
47 // Flush line on EOF
48 if (!pline) return newline();
49
50 // Measure indentation
51 for (line = *pline, idx = count = 0; isspace(line[idx]); idx++) {
52 if (line[idx]=='\t') count += 8-(count&7);
53 else if (line[idx]==' ') count++;
54 }
55 indent = idx;
56
57 // Blank lines (even with same indentation) flush line
58 if (idx==len) {
59 xputc('\n');
60 TT.level = 0;
61
62 return newline();
63 }
64
65 // Did indentation change?
66 if (count!=TT.level) newline();
67 TT.level = count;
68
69 // Loop through words
70 while (idx<len) {
71 char *word = line+idx;
72
73 // Measure this word (unicode width) and end
74 while (idx<len && !isspace(line[idx])) idx++;
75 line[idx++] = 0;
76 count = utf8len(word);
77 if (TT.pos+count+!!TT.pos>=TT.width) newline();
78
79 // When indenting a new line, preserve tab/space mixture of input
80 if (!TT.pos) {
81 TT.pos = TT.level;
82 if (indent) printf("%.*s", indent, line);
83 } else count++;
84 printf(" %s"+!(TT.pos!=TT.level), word);
85 TT.pos += count;
86 while (isspace(line[idx])) idx++;
87 }
88 }
89
fmt_main(void)90 void fmt_main(void)
91 {
92 loopfiles_lines(toys.optargs, fmt_line);
93 }
94