home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume13 / sets < prev    next >
Text File  |  1988-01-31  |  9KB  |  391 lines

  1. Subject:  v13i068:  Perform "set" operations on command line arguments
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: Chris Tweed <mcvax!caad.ed.ac.uk!chris@UUNET.UU.NET>
  7. Posting-number: Volume 13, Issue 68
  8. Archive-name: sets
  9.  
  10. I am sending you a program called 'sets' which you may wish to consider
  11. for comp.unix.sources.  Sets performs union, intersection, and difference
  12. operations on elements of two sets given on the command line.  I have
  13. found it most useful for limiting the set of filenames I want another
  14. command to work on.
  15.  
  16. For example, if I want to edit every file except those ending in '.c' I
  17. could type:
  18.     vi `sets * -d *.c`
  19. which prints out the difference between the set of all files in the
  20. current directory and the set of all '.c' files in the same directory.
  21.  
  22. Another example is to list the files in one directory which have the same
  23. names as those in another directory, i.e. the intersection of the two
  24. sets:
  25.     sets * -i ../*
  26.  
  27. Hope it's useful.
  28.     Chris Tweed
  29.     chris@caad.ed.ac.uk
  30.     ..!mcvax!ukc!edcaad!chris
  31.  
  32. # This is a shell archive.  Remove anything before this line,
  33. # then unpack it by saving it in a file and typing "sh file".
  34. # Contents:  sets.L Makefile sets.c
  35.  
  36. echo x - sets.L
  37. sed 's/^@//' > "sets.L" <<'@//E*O*F sets.L//'
  38. @.TH SETS L "December 3, 1987" "" "Local UNIX Programmer's Manual"
  39. @.UC 4
  40. @.SH NAME
  41. sets \- performs set operations on its arguments
  42. @.SH SYNOPSIS
  43. @.B sets
  44. [-p] e1 e2 ... en [\-u] [\-d] [\-i] e1 e2 ... en
  45. @.br
  46. @.SH DESCRIPTION
  47. @.I Sets
  48. prints on the standard output stream the result of a 
  49. @.B single
  50. set operation on
  51. two sets of elements provided on the command line.
  52. The sets are separated by the operator flag.
  53. The program collects the elements for each set, removes
  54. duplicated elements, and then performs the set operation.
  55. @.PP
  56. @.I Sets
  57. performs three set operations:
  58. @.TP
  59. @.B "e1 e2 ... en \-u[nion] e1 e2 ... en"
  60. prints the union of the two sets;
  61. @.TP
  62. @.B "e1 e2 ... en \-d[ifference] e1 e2 ... en"
  63. prints the set difference
  64. @.I "e1 e2 ... en"
  65. \- 
  66. @.I "e1 e2 ... en;"
  67. @.TP
  68. @.B "e1 e2 ... en \-i[ntersection] e1 e2 ... en"
  69. prints the intersection of the two sets.
  70. @.PP
  71. As
  72. @.I sets
  73. is intended to be used on filenames it ignores leading pathnames
  74. in the set operations.
  75. The
  76. @.B \-p
  77. flag makes pathnames significant in membership tests.
  78. @.SH "USAGE"
  79. @.PP
  80. @.I Sets
  81. is most useful for restricting the files to be processed by some
  82. other command.
  83. For example, to
  84. @.I grep
  85. all files in a directory except the object files you might use:
  86. @.TP
  87. grep string `sets * -d *.o`
  88. @.PP
  89. Since by default leading pathnames are ignored,
  90. @.I sets
  91. can be used across directories \- for example, to list files
  92. with the same names in two directories:
  93. @.TP
  94. sets ../* -i *
  95. @.PP
  96. Note that full pathnames are included in the output.
  97. As a result the relative position
  98. of the sets on the command line is significant.
  99. The above command will print all matching names with a leading "../".
  100. If the position of the sets is reversed only the filenames will be printed.
  101. @.SH "FILES"
  102. @.PP
  103. /usr/local/sets
  104. @.br
  105. /usr/src/local/sets.c
  106. @.\" .SH "SEE ALSO"
  107. @.\" .SH "DIAGNOSTICS"
  108. @.\" .SH "BUGS"
  109. @.SH "AUTHOR"
  110. @.PP
  111. Chris Tweed
  112. @//E*O*F sets.L//
  113. chmod u=rw,g=r,o=r sets.L
  114.  
  115. echo x - Makefile
  116. sed 's/^@//' > "Makefile" <<'@//E*O*F Makefile//'
  117. CFLAGS=-O
  118. PROG=sets
  119. FINAL=/usr/local/bin/$(PROG)
  120. MAN=/usr/man/manl/$(PROG).l
  121.  
  122. $(PROG): $(PROG).c
  123.     cc $(CFLAGS) -o $(PROG) $(PROG).c
  124.  
  125. install: $(PROG)
  126.     install -s ./$(PROG) $(FINAL)
  127.     cp $(PROG).L $(MAN)
  128. @//E*O*F Makefile//
  129. chmod u=rw,g=r,o=r Makefile
  130.  
  131. echo x - sets.c
  132. sed 's/^@//' > "sets.c" <<'@//E*O*F sets.c//'
  133. /*
  134.  * sets - performs set operations on two sets of arguments and
  135.  *        prints the result on the standard output stream
  136.  * 
  137.  * usage: sets    [-p[aths]]  e1 e2 ... en \-u[nion] e1 e2 ... en
  138.  *                    OR
  139.  *                     e1 e2 ... en \-d[ifference] e1 e2 ... en
  140.  *                    OR
  141.  *                     e1 e2 ... en \-i[ntersection] e1 e2 ... en
  142.  *
  143.  * This code may be freely distributed provided this comment
  144.  * is not removed or substantially altered.  Please mail me any
  145.  * fixes, changes, or enhancements.
  146.  *
  147.  * Christopher Tweed, EdCAAD, University of Edinburgh, Scotland.
  148.  * chris@caad.ed.ac.uk
  149.  * ..mcvax!ukc!edcaad!chris
  150.  *
  151.  * 3 December 1987.
  152.  * 
  153.  */
  154.  
  155. #include <stdio.h>
  156.  
  157. #define MAXSET    256    /* maximum size of a set */
  158.  
  159. #define STREQ(s1, s2)    (strcmp((s1), (s2)) == 0)
  160. #define NOT(p)        ((p) == FALSE)
  161. #define NAME(s)        ((ignorep == TRUE) ? nopath(s) : s)
  162.  
  163. typedef enum { FALSE=0, TRUE } BOOLEAN;
  164. typedef enum { NULL_OP=0, UNION, DIFF, INTERSECT } OPERATOR;
  165.  
  166. extern    int     strcmp();
  167. static    void    too_many();
  168. static    void    usage();
  169. static    char    *nopath();
  170. static    BOOLEAN    member();
  171. static    BOOLEAN    ignorep = TRUE;
  172.  
  173. main(argc, argv)
  174. int argc;
  175. char *argv[];
  176. {
  177.     int i, j;            /* general purpose */
  178.     BOOLEAN second = FALSE;        /* flag set after operator */
  179.     char *set1[MAXSET];        /* the first set */
  180.     int n1 = 0;            /* number of elements in first set */
  181.     char *set2[MAXSET];        /* the second set */
  182.     int n2 = 0;            /* number of elements in second set */
  183.     int n;                /* number in each set */
  184.     register OPERATOR op = NULL_OP;    /* set operation to perform */
  185.  
  186.     if (argc < 2) {
  187.         fprintf(stderr, "not enough arguments\n");
  188.         (void) usage(argv[0]);        /* EXITS */
  189.     }
  190.  
  191.     n2 = n1 = 0;
  192.     /* collect sets */
  193.     while(--argc) {
  194.         if (argv[1][0] == '-') {
  195.         second = TRUE;            /* found an operator */
  196.         switch (argv[1][1]) {
  197.             case 'u':            /* set union */
  198.             op = UNION;
  199.             break;
  200.             case 'd':            /* set difference */
  201.             op = DIFF;
  202.             break;
  203.             case 'i':            /* set intersection */
  204.             op = INTERSECT;
  205.             break;
  206.             case 'p':            /* don't ignore paths */
  207.             ignorep = FALSE;
  208.             break;
  209.             default:
  210.             fprintf(stderr, "illegal set operator %c\n",
  211.                      argv[1][1]);
  212.             (void) usage(argv[0]);    /* EXITS */
  213.         }
  214.         } else {
  215.         if (second == TRUE) {
  216.             if (n2 == MAXSET)
  217.             (void) too_many();    /* EXITS */
  218.             set2[n2++] = argv[1];
  219.         } else {
  220.             if (n1 == MAXSET)
  221.             (void) too_many();    /* EXITS */
  222.             set1[n1++] = argv[1];
  223.         }
  224.         }
  225.         argv++;
  226.     }
  227.  
  228.     if (op == NULL_OP) {
  229.         fprintf(stderr, "missing operator\n");
  230.         (void) usage(argv[0]);
  231.     }
  232.  
  233.     /* remove duplicates */
  234.     n1 = nodups(set1, n1);
  235.     n2 = nodups(set2, n2);
  236.     /*
  237.      * do set operation and print result
  238.      *
  239.      */
  240.     n = (op == UNION) ? (n1 + n2) : n1;
  241.     for (i = 0; i < n; i++) {
  242.         switch(op) {
  243.         case UNION:
  244.             j = i - n1;
  245.             if (i < n1)
  246.             printf("%s ", set1[i]);
  247.             else if (NOT(member(set2[j], set1, n1)))
  248.             printf("%s ", set2[j]);
  249.             break;
  250.         case DIFF:
  251.             if (member(set1[i], set2, n2) == FALSE) {
  252.             printf("%s ", set1[i]);
  253.             }
  254.             break;
  255.         case INTERSECT:
  256.             if (member(set1[i], set2, n2) == TRUE) {
  257.             printf("%s ", set1[i]);
  258.             }
  259.             break;
  260.         }
  261.     }
  262.  
  263.     printf("\n");
  264.     exit(0);
  265. }
  266.  
  267. /*
  268.  * nodups(set, n)
  269.  *
  270.  * removes duplicates from set of n elements and returns number
  271.  * of remaining elements in the set
  272.  *
  273.  */
  274.  
  275. int
  276. nodups(set, n)
  277. char *set[];
  278. int n;
  279. {
  280.     register int i;
  281.     register int j;
  282.     register int k;
  283.     register int nn = n;
  284.  
  285.     /*
  286.      * start at the top of the list
  287.      *
  288.      */
  289.     for(i=n-1; i>0; i--)
  290.         for(j=0; j<i; j++) {
  291.         if (set[i][0] == set[j][0] && STREQ(set[i], set[j])) {
  292.             set[i] = NULL;    /* cancel the duplicate */
  293.             /*
  294.              * move everything above
  295.              * the duplicate down one
  296.              *
  297.              */
  298.             for(k=i+1; k<nn; k++) {
  299.             set[k-1] = set[k];
  300.             set[k] = NULL;
  301.             }
  302.             nn--;
  303.             break;
  304.         }
  305.         }
  306.     return nn;
  307. }
  308.  
  309. /*
  310.  * member(s, set, n)
  311.  *
  312.  * returns TRUE if string s is a member of set which has n members
  313.  * otherwise return FALSE
  314.  *
  315.  */
  316.  
  317. static BOOLEAN
  318. member(s, set, n)
  319. register char *s, *set[];
  320. register int n;
  321. {
  322.     register int i;
  323.  
  324.     for (i = 0; i < n; i++)
  325.         if (STREQ(NAME(s), NAME(set[i])))
  326.         return TRUE;
  327.  
  328.     return FALSE;
  329. }
  330.  
  331. /*
  332.  * nopath(s)
  333.  *
  334.  * Strips leading path from s if necessary; otherwise
  335.  * returns s.
  336.  *
  337.  */
  338.  
  339. static char *
  340. nopath(s)
  341. char *s;
  342. {
  343.     extern char *rindex();
  344.     char *p;
  345.  
  346.     if (p=rindex(s, '/'))
  347.         return ++p;
  348.     else
  349.         return s;
  350. }
  351.  
  352. static void
  353. too_many()
  354. {
  355.     fprintf(stderr, "too many members\n");
  356.     exit(1);
  357. }
  358.  
  359. static void
  360. usage(prog)
  361. char *prog;
  362. {
  363.     char *set = "e1 e2 ... en";
  364.  
  365.     fprintf(stderr, "%s\t%s -u[nion] %s\n", prog, set, set);
  366.     fprintf(stderr, "\t%s -d[ifference] %s\n", set, set);
  367.     fprintf(stderr, "\t%s -i[ntersection] %s\n", set, set);
  368.     fprintf(stderr, "\t-p[aths]\t/* don't ignore leading paths */\n");
  369.     exit(1);
  370. }
  371. @//E*O*F sets.c//
  372. chmod u=rw,g=r,o=r sets.c
  373.  
  374. echo Inspecting for damage in transit...
  375. temp=/tmp/shar$$; dtemp=/tmp/.shar$$
  376. trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
  377. cat > $temp <<\!!!
  378.       74     339    1851 sets.L
  379.       11      20     202 Makefile
  380.      238     763    4758 sets.c
  381.      323    1122    6811 total
  382. !!!
  383. wc  sets.L Makefile sets.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
  384. if [ -s $dtemp ]
  385. then echo "Ouch [diff of wc output]:" ; cat $dtemp
  386. else echo "No problems found."
  387. fi
  388. exit 0
  389.  
  390.  
  391.