toybox/toys/other/fmt.c
<<
>>
Prefs
   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