master xplshn/aruu / cmd / posix / uniq.c
  1/* See LICENSE file for copyright and license details. */
  2
  3#include <ctype.h>
  4#include <stdio.h>
  5#include <stdlib.h>
  6#include <string.h>
  7
  8#include "text.h"
  9#include "util.h"
 10
 11static const char *countfmt = "";
 12static int         dflag    = 0;
 13static int         uflag    = 0;
 14static int         fskip    = 0;
 15static int         sskip    = 0;
 16
 17static struct line prevl;
 18static ssize_t     prevoff       = -1;
 19static long        prevlinecount = 0;
 20
 21static size_t
 22uniqskip(struct line *l)
 23{
 24  size_t i;
 25  int    f = fskip, s = sskip;
 26
 27  for (i = 0; i < l->len && f; --f) {
 28    while (isblank(l->data[i]))
 29      i++;
 30    while (i < l->len && !isblank(l->data[i]))
 31      i++;
 32  }
 33  for (; s && i < l->len && l->data[i] != '\n'; --s, i++)
 34    ;
 35
 36  return i;
 37}
 38
 39static void
 40uniqline(FILE *ofp, struct line *l)
 41{
 42  size_t loff;
 43
 44  if (l) {
 45    loff = uniqskip(l);
 46
 47    if (prevoff >= 0 && (l->len - loff) == (prevl.len - prevoff)
 48        && !memcmp(l->data + loff, prevl.data + prevoff, l->len - loff)) {
 49      ++prevlinecount;
 50      return;
 51    }
 52  }
 53
 54  if (prevoff >= 0) {
 55    if ((prevlinecount == 1 && !dflag) || (prevlinecount != 1 && !uflag)) {
 56      if (*countfmt)
 57        fprintf(ofp, countfmt, prevlinecount);
 58      fwrite(prevl.data, 1, prevl.len, ofp);
 59    }
 60    prevoff = -1;
 61  }
 62
 63  if (l) {
 64    if (!prevl.data || l->len >= prevl.len) {
 65      prevl.data = erealloc(prevl.data, l->len);
 66    }
 67    prevl.len = l->len;
 68    memcpy(prevl.data, l->data, prevl.len);
 69    prevoff = loff;
 70  }
 71  prevlinecount = 1;
 72}
 73
 74static void
 75uniq(FILE *fp, FILE *ofp)
 76{
 77  static struct line line;
 78  static size_t      size;
 79  ssize_t            len;
 80
 81  while ((len = getline(&line.data, &size, fp)) > 0) {
 82    line.len = len;
 83    uniqline(ofp, &line);
 84  }
 85}
 86
 87static void
 88uniqfinish(FILE *ofp)
 89{
 90  uniqline(ofp, NULL);
 91}
 92
 93static void
 94usage(void)
 95{
 96  eprintf(
 97      "usage: %s [-c] [-d | -u] [-f fields] [-s chars]"
 98      " [input [output]]\n",
 99      argv0
100  );
101}
102
103// ?man uniq: report duplicate lines
104// ?man filter out repeated lines from sorted files
105int
106main(int argc, char *argv[])
107{
108  FILE *fp[2]    = {stdin, stdout};
109  int   ret      = 0, i;
110  char *fname[2] = {"<stdin>", "<stdout>"};
111
112  ARGBEGIN
113  {
114    // ?man -c: print count or perform stdout action
115    case 'c':
116      countfmt = "%7ld ";
117      break;
118    // ?man -d: specify directory
119    case 'd':
120      dflag = 1;
121      break;
122    // ?man -u: unbuffered output
123    case 'u':
124      uflag = 1;
125      break;
126    // ?man -f:num: force the operation
127    case 'f':
128      fskip = estrtonum(EARGF(usage()), 0, INT_MAX);
129      break;
130    // ?man -s:num: silent mode or print summary
131    case 's':
132      sskip = estrtonum(EARGF(usage()), 0, INT_MAX);
133      break;
134    default:
135      usage();
136  }
137  ARGEND
138
139  if (argc > 2)
140    usage();
141
142  for (i = 0; i < argc; i++) {
143    if (strcmp(argv[i], "-")) {
144      fname[i] = argv[i];
145      if (!(fp[i] = fopen(argv[i], (i == 0) ? "r" : "w")))
146        eprintf("fopen %s:", argv[i]);
147    }
148  }
149
150  uniq(fp[0], fp[1]);
151  uniqfinish(fp[1]);
152
153  ret |= fshut(fp[0], fname[0]) | fshut(fp[1], fname[1]);
154
155  return ret;
156}