toybox/toys/posix/uniq.c
<<
>>
Prefs
   1/* uniq.c - report or filter out repeated lines in a file
   2 *
   3 * Copyright 2012 Georgi Chorbadzhiyski <georgi@unixsol.org>
   4 *
   5 * See http://opengroup.org/onlinepubs/9699919799/utilities/uniq.html
   6
   7USE_UNIQ(NEWTOY(uniq, "f#s#w#zicdu", TOYFLAG_USR|TOYFLAG_BIN))
   8
   9config UNIQ
  10  bool "uniq"
  11  default y
  12  help
  13    usage: uniq [-cduiz] [-w maxchars] [-f fields] [-s char] [input_file [output_file]]
  14
  15    Report or filter out repeated lines in a file
  16
  17    -c  show counts before each line
  18    -d  show only lines that are repeated
  19    -u  show only lines that are unique
  20    -i  ignore case when comparing lines
  21    -z  lines end with \0 not \n
  22    -w  compare maximum X chars per line
  23    -f  ignore first X fields
  24    -s  ignore first X chars
  25*/
  26
  27#define FOR_uniq
  28#include "toys.h"
  29
  30GLOBALS(
  31  long maxchars;
  32  long nchars;
  33  long nfields;
  34  long repeats;
  35)
  36
  37static char *skip(char *str)
  38{
  39  long nchars = TT.nchars, nfields;
  40
  41  // Skip fields first
  42  for (nfields = TT.nfields; nfields; str++) {
  43    while (*str && isspace(*str)) str++;
  44    while (*str && !isspace(*str)) str++;
  45    nfields--;
  46  }
  47  // Skip chars
  48  while (*str && nchars--) str++;
  49
  50  return str;
  51}
  52
  53static void print_line(FILE *f, char *line)
  54{
  55  if (toys.optflags & (TT.repeats ? FLAG_u : FLAG_d)) return;
  56  if (toys.optflags & FLAG_c) fprintf(f, "%7lu ", TT.repeats + 1);
  57  fputs(line, f);
  58  if (toys.optflags & FLAG_z) fputc(0, f);
  59}
  60
  61void uniq_main(void)
  62{
  63  FILE *infile = stdin, *outfile = stdout;
  64  char *thisline = NULL, *prevline = NULL, *tmpline, eol = '\n';
  65  size_t thissize, prevsize = 0, tmpsize;
  66
  67  if (toys.optc >= 1) infile = xfopen(toys.optargs[0], "r");
  68  if (toys.optc >= 2) outfile = xfopen(toys.optargs[1], "w");
  69
  70  if (toys.optflags & FLAG_z) eol = 0;
  71
  72  // If first line can't be read
  73  if (getdelim(&prevline, &prevsize, eol, infile) < 0)
  74    return;
  75
  76  while (getdelim(&thisline, &thissize, eol, infile) > 0) {
  77    int diff;
  78    char *t1, *t2;
  79
  80    // If requested get the chosen fields + character offsets.
  81    if (TT.nfields || TT.nchars) {
  82      t1 = skip(thisline);
  83      t2 = skip(prevline);
  84    } else {
  85      t1 = thisline;
  86      t2 = prevline;
  87    }
  88
  89    if (TT.maxchars == 0) {
  90      diff = !(toys.optflags & FLAG_i) ? strcmp(t1, t2) : strcasecmp(t1, t2);
  91    } else {
  92      diff = !(toys.optflags & FLAG_i) ? strncmp(t1, t2, TT.maxchars)
  93              : strncasecmp(t1, t2, TT.maxchars);
  94    }
  95
  96    if (diff == 0) { // same
  97      TT.repeats++;
  98    } else {
  99      print_line(outfile, prevline);
 100
 101      TT.repeats = 0;
 102
 103      tmpline = prevline;
 104      prevline = thisline;
 105      thisline = tmpline;
 106
 107      tmpsize = prevsize;
 108      prevsize = thissize;
 109      thissize = tmpsize;
 110    }
 111  }
 112
 113  print_line(outfile, prevline);
 114
 115  if (CFG_TOYBOX_FREE) {
 116    if (outfile != stdout) fclose(outfile);
 117    if (infile != stdin) fclose(infile);
 118    free(prevline);
 119    free(thisline);
 120  }
 121}
 122