10f66f451Sopenharmony_ci/* fmt.c - Text formatter
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2017 The Android Open Source Project
40f66f451Sopenharmony_ci *
50f66f451Sopenharmony_ci * No standard.
60f66f451Sopenharmony_ci *
70f66f451Sopenharmony_ci * Only counts space and tab for indent level (eats other low ascii chars,
80f66f451Sopenharmony_ci * treats all UTF8 chars as non-whitespace), preserves indentation but squashes
90f66f451Sopenharmony_ci * together runs of whitespace. No header/footer logic, no end-of-sentence
100f66f451Sopenharmony_ci * double-space, preserves initial tab/space mix when indenting new lines.
110f66f451Sopenharmony_ci
120f66f451Sopenharmony_ciUSE_FMT(NEWTOY(fmt, "w#<0=75", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
130f66f451Sopenharmony_ci
140f66f451Sopenharmony_ciconfig FMT
150f66f451Sopenharmony_ci  bool "fmt"
160f66f451Sopenharmony_ci  default y
170f66f451Sopenharmony_ci  help
180f66f451Sopenharmony_ci    usage: fmt [-w WIDTH] [FILE...]
190f66f451Sopenharmony_ci
200f66f451Sopenharmony_ci    Reformat input to wordwrap at a given line length, preserving existing
210f66f451Sopenharmony_ci    indentation level, writing to stdout.
220f66f451Sopenharmony_ci
230f66f451Sopenharmony_ci    -w WIDTH	Maximum characters per line (default 75)
240f66f451Sopenharmony_ci*/
250f66f451Sopenharmony_ci
260f66f451Sopenharmony_ci#define FOR_fmt
270f66f451Sopenharmony_ci#include "toys.h"
280f66f451Sopenharmony_ci
290f66f451Sopenharmony_ciGLOBALS(
300f66f451Sopenharmony_ci  int width;
310f66f451Sopenharmony_ci
320f66f451Sopenharmony_ci  int level, pos;
330f66f451Sopenharmony_ci)
340f66f451Sopenharmony_ci
350f66f451Sopenharmony_cistatic void newline(void)
360f66f451Sopenharmony_ci{
370f66f451Sopenharmony_ci  if (TT.pos) xputc('\n');
380f66f451Sopenharmony_ci  TT.pos = 0;
390f66f451Sopenharmony_ci}
400f66f451Sopenharmony_ci
410f66f451Sopenharmony_ci// Process lines of input, with (0,0) flush between files
420f66f451Sopenharmony_cistatic void fmt_line(char **pline, long len)
430f66f451Sopenharmony_ci{
440f66f451Sopenharmony_ci  char *line;
450f66f451Sopenharmony_ci  int idx, indent, count;
460f66f451Sopenharmony_ci
470f66f451Sopenharmony_ci  // Flush line on EOF
480f66f451Sopenharmony_ci  if (!pline) return newline();
490f66f451Sopenharmony_ci
500f66f451Sopenharmony_ci  // Measure indentation
510f66f451Sopenharmony_ci  for (line = *pline, idx = count = 0; isspace(line[idx]); idx++) {
520f66f451Sopenharmony_ci    if (line[idx]=='\t') count += 8-(count&7);
530f66f451Sopenharmony_ci    else if (line[idx]==' ') count++;
540f66f451Sopenharmony_ci  }
550f66f451Sopenharmony_ci  indent = idx;
560f66f451Sopenharmony_ci
570f66f451Sopenharmony_ci  // Blank lines (even with same indentation) flush line
580f66f451Sopenharmony_ci  if (idx==len) {
590f66f451Sopenharmony_ci    xputc('\n');
600f66f451Sopenharmony_ci    TT.level = 0;
610f66f451Sopenharmony_ci
620f66f451Sopenharmony_ci    return newline();
630f66f451Sopenharmony_ci  }
640f66f451Sopenharmony_ci
650f66f451Sopenharmony_ci  // Did indentation change?
660f66f451Sopenharmony_ci  if (count!=TT.level) newline();
670f66f451Sopenharmony_ci  TT.level = count;
680f66f451Sopenharmony_ci
690f66f451Sopenharmony_ci  // Loop through words
700f66f451Sopenharmony_ci  while (idx<len) {
710f66f451Sopenharmony_ci    char *word = line+idx;
720f66f451Sopenharmony_ci
730f66f451Sopenharmony_ci    // Measure this word (unicode width) and end
740f66f451Sopenharmony_ci    while (idx<len && !isspace(line[idx])) idx++;
750f66f451Sopenharmony_ci    line[idx++] = 0;
760f66f451Sopenharmony_ci    count = utf8len(word);
770f66f451Sopenharmony_ci    if (TT.pos+count+!!TT.pos>=TT.width) newline();
780f66f451Sopenharmony_ci
790f66f451Sopenharmony_ci    // When indenting a new line, preserve tab/space mixture of input
800f66f451Sopenharmony_ci    if (!TT.pos) {
810f66f451Sopenharmony_ci      TT.pos = TT.level;
820f66f451Sopenharmony_ci      if (indent) printf("%.*s", indent, line);
830f66f451Sopenharmony_ci    } else count++;
840f66f451Sopenharmony_ci    printf(" %s"+!(TT.pos!=TT.level), word);
850f66f451Sopenharmony_ci    TT.pos += count;
860f66f451Sopenharmony_ci    while (isspace(line[idx])) idx++;
870f66f451Sopenharmony_ci  }
880f66f451Sopenharmony_ci}
890f66f451Sopenharmony_ci
900f66f451Sopenharmony_civoid fmt_main(void)
910f66f451Sopenharmony_ci{
920f66f451Sopenharmony_ci  loopfiles_lines(toys.optargs, fmt_line);
930f66f451Sopenharmony_ci}
94