master xplshn/aruu / cmd / posix / fold.c
  1/* See LICENSE file for copyright and license details. */
  2
  3#include <ctype.h>
  4#include <stdint.h>
  5#include <stdio.h>
  6#include <stdlib.h>
  7#include <string.h>
  8
  9#include "text.h"
 10#include "utf.h"
 11#include "util.h"
 12
 13static int    bflag = 0;
 14static int    sflag = 0;
 15static size_t width = 80;
 16
 17static void
 18foldline(struct line *l, const char *fname)
 19{
 20  size_t i, col, last, spacesect, len;
 21  Rune   r;
 22  int    runelen;
 23
 24  for (i = 0, last = 0, col = 0, spacesect = 0; i < l->len; i += runelen) {
 25    if (col >= width && ((l->data[i] != '\r' && l->data[i] != '\b') || bflag)) {
 26      if (bflag && col > width)
 27        i -= runelen; /* never split a character */
 28      len = ((sflag && spacesect) ? spacesect : i) - last;
 29      if (fwrite(l->data + last, 1, len, stdout) != len)
 30        eprintf("fwrite <stdout>:");
 31      if (l->data[i] != '\n')
 32        putchar('\n');
 33      if (sflag && spacesect)
 34        i = spacesect;
 35      last      = i;
 36      col       = 0;
 37      spacesect = 0;
 38    }
 39    runelen = charntorune(&r, l->data + i, l->len - i);
 40    if (!runelen || r == Runeerror)
 41      eprintf("charntorune: %s: invalid utf\n", fname);
 42    if (sflag && isblankrune(r))
 43      spacesect = i + runelen;
 44    if (!bflag && iscntrl(l->data[i])) {
 45      switch (l->data[i]) {
 46        case '\b':
 47          col -= (col > 0);
 48          break;
 49        case '\r':
 50          col = 0;
 51          break;
 52        case '\t':
 53          col += (8 - (col % 8));
 54          if (col >= width)
 55            i--;
 56          break;
 57      }
 58    } else {
 59      col += bflag ? runelen : 1;
 60    }
 61  }
 62  if (l->len - last)
 63    fwrite(l->data + last, 1, l->len - last, stdout);
 64}
 65
 66static void
 67fold(FILE *fp, const char *fname)
 68{
 69  static struct line line;
 70  static size_t      size = 0;
 71  ssize_t            len;
 72
 73  while ((len = getline(&line.data, &size, fp)) > 0) {
 74    line.len = len;
 75    foldline(&line, fname);
 76  }
 77  if (ferror(fp))
 78    eprintf("getline %s:", fname);
 79}
 80
 81static void
 82usage(void)
 83{
 84  eprintf("usage: %s [-bs] [-w num | -num] [FILE ...]\n", argv0);
 85}
 86
 87// ?man fold: wrap lines to fit width
 88// ?man arguments: FILE ...
 89// ?man wrap input lines to fit a specified width
 90int
 91main(int argc, char *argv[])
 92{
 93  FILE *fp;
 94  int   ret = 0;
 95
 96  ARGBEGIN
 97  {
 98    // ?man -b: specify block size or base directory
 99    case 'b':
100      bflag = 1;
101      break;
102    // ?man -s: silent mode or print summary
103    case 's':
104      sflag = 1;
105      break;
106    // ?man -w:num: wait for completion
107    case 'w':
108      width = estrtonum(
109          EARGF(usage()), 1, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX)
110      );
111      break;
112    // ?man ARGNUM: specify RGNUM option
113    ARGNUM:
114      if (!(width = ARGNUMF()))
115        eprintf("illegal width value, too small\n");
116      break;
117    default:
118      usage();
119  }
120  ARGEND
121
122  if (!argc) {
123    fold(stdin, "<stdin>");
124  } else {
125    for (; *argv; argc--, argv++) {
126      if (!strcmp(*argv, "-")) {
127        *argv = "<stdin>";
128        fp    = stdin;
129      } else if (!(fp = fopen(*argv, "r"))) {
130        weprintf("fopen %s:", *argv);
131        ret = 1;
132        continue;
133      }
134      fold(fp, *argv);
135      if (fp != stdin && fshut(fp, *argv))
136        ret = 1;
137    }
138  }
139
140  ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
141
142  return ret;
143}