master xplshn/aruu / cmd / pseudo / cols.c
  1/* See LICENSE file for copyright and license details. */
  2
  3#include <sys/ioctl.h>
  4
  5#include <limits.h>
  6#include <stdint.h>
  7#include <stdio.h>
  8#include <stdlib.h>
  9#include <string.h>
 10#include <unistd.h>
 11
 12#include "text.h"
 13#include "util.h"
 14
 15static void
 16usage(void)
 17{
 18  eprintf("usage: %s [-c num] [file ...]\n", argv0);
 19}
 20
 21// ?man cols: format columns
 22// ?man arguments: file ...
 23// ?man format standard input into vertical columns
 24int
 25main(int argc, char *argv[])
 26{
 27  FILE          *fp;
 28  struct winsize w;
 29  struct linebuf b     = EMPTY_LINEBUF;
 30  size_t         chars = 65, maxlen = 0, i, j, k, len, cols, rows;
 31  int            cflag = 0, ret = 0;
 32  char          *p;
 33
 34  ARGBEGIN
 35  {
 36    // ?man -c:num: print count or perform stdout action
 37    case 'c':
 38      cflag = 1;
 39      chars = estrtonum(
 40          EARGF(usage()), 1, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX)
 41      );
 42      break;
 43    default:
 44      usage();
 45  }
 46  ARGEND
 47
 48  if (!cflag) {
 49    if ((p = getenv("COLUMNS")))
 50      chars = estrtonum(p, 1, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
 51    else if (!ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) && w.ws_col > 0)
 52      chars = w.ws_col;
 53  }
 54
 55  if (!argc) {
 56    getlines(stdin, &b);
 57  } else {
 58    for (; *argv; argc--, argv++) {
 59      if (!strcmp(*argv, "-")) {
 60        *argv = "<stdin>";
 61        fp    = stdin;
 62      } else if (!(fp = fopen(*argv, "r"))) {
 63        weprintf("fopen %s:", *argv);
 64        ret = 1;
 65        continue;
 66      }
 67      getlines(fp, &b);
 68      if (fp != stdin && fshut(fp, *argv))
 69        ret = 1;
 70    }
 71  }
 72
 73  for (i = 0; i < b.nlines; i++) {
 74    for (j = 0, len = 0; j < b.lines[i].len; j++) {
 75      if (UTF8_POINT(b.lines[i].data[j]))
 76        len++;
 77    }
 78    if (len && b.lines[i].data[b.lines[i].len - 1] == '\n') {
 79      b.lines[i].data[--(b.lines[i].len)] = '\0';
 80      len--;
 81    }
 82    if (len > maxlen)
 83      maxlen = len;
 84  }
 85
 86  for (cols = 1; (cols + 1) * maxlen + cols <= chars; cols++)
 87    ;
 88  rows = b.nlines / cols + (b.nlines % cols > 0);
 89
 90  for (i = 0; i < rows; i++) {
 91    for (j = 0; j < cols && i + j * rows < b.nlines; j++) {
 92      for (k = 0, len = 0; k < b.lines[i + j * rows].len; k++) {
 93        if (UTF8_POINT(b.lines[i + j * rows].data[k]))
 94          len++;
 95      }
 96      fwrite(b.lines[i + j * rows].data, 1, b.lines[i + j * rows].len, stdout);
 97      if (j < cols - 1)
 98        for (k = len; k < maxlen + 1; k++)
 99          putchar(' ');
100    }
101    putchar('\n');
102  }
103
104  ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
105
106  return ret;
107}