master xplshn/aruu / cmd / posix / split.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 "util.h"
 10
 11static int base = 26, start = 'a';
 12
 13static int
 14itostr(char *str, int x, int n)
 15{
 16  str[n] = '\0';
 17  while (n-- > 0) {
 18    str[n] = start + (x % base);
 19    x /= base;
 20  }
 21
 22  return x ? -1 : 0;
 23}
 24
 25static FILE *
 26nextfile(FILE *f, char *buf, int plen, int slen)
 27{
 28  static int filecount = 0;
 29
 30  if (f)
 31    fshut(f, "<file>");
 32  if (itostr(buf + plen, filecount++, slen) < 0)
 33    return NULL;
 34
 35  if (!(f = fopen(buf, "w")))
 36    eprintf("'%s':", buf);
 37
 38  return f;
 39}
 40
 41static void
 42usage(void)
 43{
 44  eprintf(
 45      "usage: %s [-a num] [-b num[k|m|g] | -l num] [-d] "
 46      "[file [prefix]]\n",
 47      argv0
 48  );
 49}
 50
 51// ?man split: split file into pieces
 52// ?man arguments: | -l num
 53// ?man split a file into fixed-size pieces
 54int
 55main(int argc, char *argv[])
 56{
 57  FILE *in = stdin, *out = NULL;
 58  off_t size = 1000, n;
 59  int   ret = 0, ch, plen, slen = 2, always = 0;
 60  char  name[NAME_MAX + 1], *prefix = "x", *file = NULL;
 61
 62  ARGBEGIN
 63  {
 64    // ?man -a:num: print or show all entries
 65    case 'a':
 66      slen = estrtonum(EARGF(usage()), 0, INT_MAX);
 67      break;
 68    // ?man -b:str: specify block size or base directory
 69    case 'b':
 70      always = 1;
 71      if ((size = parseoffset(EARGF(usage()))) < 0)
 72        return 1;
 73      if (!size)
 74        eprintf("size needs to be positive\n");
 75      break;
 76    // ?man -d: specify directory
 77    case 'd':
 78      base  = 10;
 79      start = '0';
 80      break;
 81    // ?man -l:num: list in long format
 82    case 'l':
 83      always = 0;
 84      size   = estrtonum(EARGF(usage()), 1, MIN(LLONG_MAX, SSIZE_MAX));
 85      break;
 86    default:
 87      usage();
 88  }
 89  ARGEND
 90
 91  if (*argv)
 92    file = *argv++;
 93  if (*argv)
 94    prefix = *argv++;
 95  if (*argv)
 96    usage();
 97
 98  plen = strlen(prefix);
 99  if (plen + slen > NAME_MAX)
100    eprintf("names cannot exceed %d bytes\n", NAME_MAX);
101  estrlcpy(name, prefix, sizeof(name));
102
103  if (file && strcmp(file, "-")) {
104    if (!(in = fopen(file, "r")))
105      eprintf("fopen %s:", file);
106  }
107
108  n = 0;
109  while ((ch = getc(in)) != EOF) {
110    if (!out || n >= size) {
111      if (!(out = nextfile(out, name, plen, slen)))
112        eprintf("fopen: %s:", name);
113      n = 0;
114    }
115    n += (always || ch == '\n');
116    putc(ch, out);
117  }
118
119  ret |= (in != stdin) && fshut(in, "<infile>");
120  ret |= out && (out != stdout) && fshut(out, "<outfile>");
121  ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
122
123  return ret;
124}