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