xref: /third_party/toybox/toys/other/fmt.c (revision 0f66f451)
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
12USE_FMT(NEWTOY(fmt, "w#<0=75", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
13
14config 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
29GLOBALS(
30  int width;
31
32  int level, pos;
33)
34
35static 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
42static 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
90void fmt_main(void)
91{
92  loopfiles_lines(toys.optargs, fmt_line);
93}
94