uniq.c 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  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. USE_UNIQ(NEWTOY(uniq, "f#s#w#zicdu", TOYFLAG_USR|TOYFLAG_BIN))
  7. config UNIQ
  8. bool "uniq"
  9. default y
  10. help
  11. usage: uniq [-cduiz] [-w MAXCHARS] [-f FIELDS] [-s CHAR] [INFILE [OUTFILE]]
  12. Report or filter out repeated lines in a file
  13. -c Show counts before each line
  14. -d Show only lines that are repeated
  15. -u Show only lines that are unique
  16. -i Ignore case when comparing lines
  17. -z Lines end with \0 not \n
  18. -w Compare maximum X chars per line
  19. -f Ignore first X fields
  20. -s Ignore first X chars
  21. */
  22. #define FOR_uniq
  23. #include "toys.h"
  24. GLOBALS(
  25. long w, s, f;
  26. long repeats;
  27. )
  28. static char *skip(char *str)
  29. {
  30. long nchars = TT.s, nfields = TT.f;
  31. // Skip fields first
  32. while (nfields--) {
  33. while (*str && isspace(*str)) str++;
  34. while (*str && !isspace(*str)) str++;
  35. }
  36. // Skip chars
  37. while (*str && nchars--) str++;
  38. return str;
  39. }
  40. static void print_line(FILE *f, char *line)
  41. {
  42. if (TT.repeats ? FLAG(u) : FLAG(d)) return;
  43. if (FLAG(c)) fprintf(f, "%7lu ", TT.repeats + 1);
  44. fputs(line, f);
  45. if (FLAG(z)) fputc(0, f);
  46. }
  47. void uniq_main(void)
  48. {
  49. FILE *infile = stdin, *outfile = stdout;
  50. char *thisline = 0, *prevline = 0, *tmpline, eol = '\n';
  51. size_t thissize, prevsize = 0, tmpsize;
  52. if (toys.optc >= 1) infile = xfopen(toys.optargs[0], "r");
  53. if (toys.optc >= 2) outfile = xfopen(toys.optargs[1], "w");
  54. if (FLAG(z)) eol = 0;
  55. // If first line can't be read
  56. if (getdelim(&prevline, &prevsize, eol, infile) < 0) return;
  57. while (getdelim(&thisline, &thissize, eol, infile) > 0) {
  58. int diff;
  59. char *t1, *t2;
  60. // If requested get the chosen fields + character offsets.
  61. if (TT.f || TT.s) {
  62. t1 = skip(thisline);
  63. t2 = skip(prevline);
  64. } else {
  65. t1 = thisline;
  66. t2 = prevline;
  67. }
  68. if (!TT.w)
  69. diff = !FLAG(i) ? strcmp(t1, t2) : strcasecmp(t1, t2);
  70. else diff = !FLAG(i) ? strncmp(t1, t2, TT.w) : strncasecmp(t1, t2, TT.w);
  71. if (!diff) TT.repeats++;
  72. else {
  73. print_line(outfile, prevline);
  74. TT.repeats = 0;
  75. tmpline = prevline;
  76. prevline = thisline;
  77. thisline = tmpline;
  78. tmpsize = prevsize;
  79. prevsize = thissize;
  80. thissize = tmpsize;
  81. }
  82. }
  83. print_line(outfile, prevline);
  84. if (CFG_TOYBOX_FREE) {
  85. if (outfile != stdout) fclose(outfile);
  86. if (infile != stdin) fclose(infile);
  87. free(prevline);
  88. free(thisline);
  89. }
  90. }