]> andersk Git - moira.git/blob - clients/blanche/blanche.c
Don't do readline stuff if _either_ stdin or stdout isn't a tty
[moira.git] / clients / blanche / blanche.c
1 /* $Header$
2  *
3  * Command line oriented Moira List tool.
4  *
5  * by Mark Rosenstein, September 1988.
6  *
7  * Copyright 1989 by the Massachusetts Institute of Technology.
8  *
9  * (c) Copyright 1988 by the Massachusetts Institute of Technology.
10  * For copying and distribution information, please see the file
11  * <mit-copyright.h>.
12  */
13
14 /* ### Aren't there a lot of sq abstraction barrier violations here?
15    Do we need to improve the support for queue operations? */
16
17 #include <mit-copyright.h>
18 #include <errno.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <ctype.h>
22 #include <string.h>
23 #include <moira.h>
24 #include <moira_site.h>
25
26 #ifndef LINT
27 static char blanche_rcsid[] = "$Header$";
28 #endif
29
30
31 struct member {
32     int type;
33     char *name;
34 };
35
36 /* It is important to membercmp that M_USER < M_LIST < M_STRING */
37 #define M_ANY           0
38 #define M_USER          1
39 #define M_LIST          2
40 #define M_STRING        3
41 #define M_KERBEROS      4
42
43 /* argument parsing macro */
44 #define argis(a,b) ((strcmp(*arg+1, a) == 0) || (strcmp(*arg+1, b) == 0))
45
46 /* flags from command line */
47 int infoflg, verbose, syncflg, memberflg, recursflg, debugflg, noauth;
48 int showusers, showstrings, showkerberos, showlists;
49
50 /* various member lists */
51 struct save_queue *addlist, *dellist, *memberlist, *synclist;
52
53 char *listname, *whoami;
54
55 #ifndef POSIX
56 extern int errno;
57 #endif
58
59 int show_list_info(), show_list_count(), get_list_members(), scream();
60 int show_list_members(), membercmp();
61 struct member *parse_member();
62
63
64
65 main(argc, argv)
66 int argc;
67 char **argv;
68 {
69     int status;
70     char **arg = argv;
71     char *membervec[3], *motd;
72     struct member *memberstruct;
73     char *server = NULL, *p;
74
75     /* clear all flags & lists */
76     infoflg = verbose = syncflg = memberflg = debugflg = recursflg = 0;
77     noauth = showusers = showstrings = showkerberos = showlists = 0;
78     listname = NULL;
79     addlist = sq_create();
80     dellist = sq_create();
81     memberlist = sq_create();
82     synclist = sq_create();
83     whoami = argv[0];
84
85     /* parse args, building addlist, dellist, & synclist */
86     while (++arg - argv < argc) {
87         if  (**arg == '-')
88         {
89             if (argis("m", "members"))
90                 memberflg++;
91             else if (argis("u", "users"))
92                 showusers++;
93             else if (argis("s", "strings"))
94                 showstrings++;
95             else if (argis("l", "lists"))
96                 showlists++;
97             else if (argis("k", "kerberos"))
98                 showkerberos++;
99             else if (argis("D", "debug"))
100                 debugflg++;
101             else if (argis("i","info"))
102               infoflg++;
103             else if (argis("n","noauth"))
104               noauth++;
105             else if (argis("v","verbose"))
106               verbose++;
107             else if (argis("r","recursive"))
108               recursflg++;
109             else if (argis("S","server") || argis("db","database"))
110                 if (arg - argv < argc - 1) {
111                     ++arg;
112                     server = *arg;
113                 } else
114                     usage(argv);
115             else if (argis("a","add"))
116                 if (arg - argv < argc - 1) {
117                     ++arg;
118                     if (memberstruct = parse_member(*arg))
119                         sq_save_data(addlist, memberstruct);
120                 } else
121                     usage(argv);
122             else if (argis("al","addlist"))
123                 if (arg - argv < argc - 1) {
124                     ++arg;
125                     get_members_from_file(*arg, addlist);
126                 } else
127                   usage(argv);
128             else if (argis("d","delete"))
129                 if (arg - argv < argc - 1) {
130                     ++arg;
131                     if (memberstruct = parse_member(*arg))
132                         sq_save_data(dellist, memberstruct);
133                 } else
134                     usage(argv);
135             else if (argis("dl","deletelist"))
136                 if (arg - argv < argc - 1) {
137                     ++arg;
138                     get_members_from_file(*arg, dellist);
139                 } else
140                   usage(argv);
141             else if (argis("f","file"))
142                 if (arg - argv < argc - 1) {
143                     syncflg++;
144                     ++arg;
145                     get_members_from_file(*arg, synclist);
146                 } else
147                   usage(argv);
148             else
149                 usage(argv);
150         }
151         else if (listname == NULL)
152           listname = *arg;
153         else
154           usage(argv);
155     }
156     if (listname == NULL)
157       usage(argv);
158
159     /* if no other options specified, turn on list members flag */
160     if (!(infoflg || syncflg ||
161           addlist->q_next != addlist || dellist->q_next != dellist))
162       memberflg++;
163
164     /* If none of {users,strings,lists,kerberos} specified, turn them all on */
165     if (!(showusers || showstrings || showlists || showkerberos))
166       showusers = showstrings = showlists = showkerberos = 1;
167
168     /* fire up Moira */
169     if (status = mr_connect(server)) {
170         com_err(whoami, status, "unable to connect to the Moira server");
171         exit(2);
172     }
173     if ( status = mr_motd(&motd) ) {
174         com_err(whoami, status, "unable to check server status");
175         exit(2);
176     }
177     if (motd) {
178         fprintf(stderr, "The Moira server is currently unavailable:\n%s\n", motd);
179         mr_disconnect();
180         exit(2);
181     }
182
183     if (!noauth && (status = mr_auth("blanche"))) {
184         if (status == MR_USER_AUTH)
185           com_err(whoami, status, "");
186         else {
187             com_err(whoami, status, "unable to authenticate to Moira");
188             com_err(whoami, 0,
189                     " Try the -noauth flag if you don't need authentication");
190             exit(2);
191         }
192     }
193
194     /* display list info if requested to */
195     if (infoflg) {
196         status = mr_query("get_list_info", 1, &listname, show_list_info,NULL);
197         if (status)
198           com_err(whoami, status, "while getting list information");
199         if (verbose && !memberflg) {
200             status = mr_query("count_members_of_list", 1, &listname,
201                                show_list_count, NULL);
202             if (status)
203               com_err(whoami, status, "while getting list count");
204         }
205     }
206
207     /* if we're synchronizing to a file, we need to:
208      *  get the current members of the list
209      *  for each member of the sync file
210      *     if they are on the list, remove them from the in-memory copy
211      *     if they're not on the list, add them to add-list
212      *  if anyone is left on the in-memory copy, put them on the delete-list
213      * lastly, reset memberlist so we can use it again later
214      */
215     if (syncflg) {
216         status = mr_query("get_members_of_list", 1, &listname,
217                            get_list_members, (char *)memberlist);
218         if (status) {
219             com_err(whoami, status, "getting members of list %s", listname);
220             exit (2);
221         }
222         while (sq_get_data(synclist, &memberstruct)) {
223             struct save_queue *q;
224             int removed = 0;
225
226             for (q = memberlist->q_next; q != memberlist; q = q->q_next) {
227                 if (membercmp(q->q_data, memberstruct) == 0) {
228                     q->q_prev->q_next = q->q_next;
229                     q->q_next->q_prev = q->q_prev;
230                     removed++;
231                     break;
232                 }
233             }
234             if (!removed)
235               sq_save_data(addlist, memberstruct);
236         }
237         while (sq_get_data(memberlist, &memberstruct))
238           sq_save_data(dellist, memberstruct);
239         sq_destroy(memberlist);
240         memberlist = sq_create();
241     }
242
243     /* Process the add list */
244     while (sq_get_data(addlist, &memberstruct)) {
245         if ((memberstruct->type == M_STRING ||
246              memberstruct->type == M_ANY) &&
247             strchr(memberstruct->name, '\'')) {
248                 fprintf(stderr, "%s: Illegal character \"'\" in argument while adding \"STRING:%s\" to %s.\n",
249                         whoami, memberstruct->name, listname);
250                 continue;
251         }
252         /* canonicalize string if necessary */
253         if (memberstruct->type == M_STRING &&
254             (p = strchr(memberstruct->name, '@'))) {
255             char *host = canonicalize_hostname(strsave(++p));
256             static char **mailhubs = NULL;
257             char *argv[4];
258             int i, collect();
259
260             if (!mailhubs) {
261                 argv[0] = "mailhub";
262                 argv[1] = "TYPE";
263                 argv[2] = "*";
264                 mailhubs = (char **)malloc(sizeof(char *));
265                 mailhubs[0] = NULL;
266                 status = mr_query("get_alias", 3, argv, collect,
267                                    (char *)&mailhubs);
268                 if (status != MR_SUCCESS && status != MR_NO_MATCH) {
269                     com_err(whoami, status,
270                             " while reading list of MAILHUB servers");
271                     mailhubs[0] = NULL;
272                 }
273             }
274             for (i = 0; p = mailhubs[i]; i++) {
275                 if (!strcasecmp(p, host)) {
276                     host = strsave(memberstruct->name);
277                     *(strchr(memberstruct->name, '@')) = 0;
278                     memberstruct->type = M_ANY;
279                     fprintf(stderr, "Warning: \"STRING:%s\" converted to \"%s\" because it is a local name.\n",
280                             host, memberstruct->name);
281                     break;
282                 }
283             }
284             free(host);
285         }
286         /* now continue adding member */
287         membervec[0] = listname;
288         membervec[2] = memberstruct->name;
289         if (verbose) {
290             printf("Adding member ");
291             show_list_member(memberstruct);
292         }
293         switch (memberstruct->type) {
294         case M_ANY:
295         case M_USER:
296             membervec[1] = "USER";
297             status = mr_query("add_member_to_list", 3, membervec, scream, NULL);
298             if (status == MR_SUCCESS)
299               break;
300             else if (status != MR_USER || memberstruct->type != M_ANY) {
301                 com_err(whoami, status, "while adding member %s to %s",
302                         memberstruct->name, listname);
303                 break;
304             }
305         case M_LIST:
306             membervec[1] = "LIST";
307             status = mr_query("add_member_to_list", 3, membervec,
308                                scream, NULL);
309             if (status == MR_SUCCESS) {
310                 if (!strcmp(membervec[0], getenv("USER"))) {
311                     fprintf(stderr, "\nWARNING: \"LIST:%s\" was just added to list \"%s\".\n",
312                             membervec[2], membervec[0]);
313                     fprintf(stderr, "If you meant to add yourself to the list \"%s\", type:\n", membervec[2]);
314                     fprintf(stderr, "\tblanche %s -d %s\t(to undo this)\n",
315                             membervec[0], membervec[2]);
316                     fprintf(stderr, "\tblanche %s -a %s\t(to add yourself to that list)\n",
317                             membervec[2], membervec[0]);
318                 }
319                 break;
320             } else if (status != MR_LIST || memberstruct->type != M_ANY) {
321                 com_err(whoami, status, "while adding member %s to %s",
322                         memberstruct->name, listname);
323                 break;
324             }
325         case M_STRING:
326             membervec[1] = "STRING";
327             status = mr_query("add_member_to_list", 3, membervec,
328                                scream, NULL);
329             if (status != MR_SUCCESS)
330               com_err(whoami, status, "while adding member %s to %s",
331                       memberstruct->name, listname);
332             else if (!strchr(memberstruct->name, '@') &&
333                      !strchr(memberstruct->name, '!') &&
334                      !strchr(memberstruct->name, '%')) {
335                 fprintf(stderr, "\nWARNING: \"STRING:%s\" was just added to list \"%s\".\n",
336                         memberstruct->name, listname);
337                 fprintf(stderr, "\tIf %s is a mailing list, this may cause it to stop working.\n", listname);
338                 fprintf(stderr, "\tYou should consider removing \"STRING:%s\" from the list.\n", memberstruct->name);
339             }
340             break;
341         case M_KERBEROS:
342             membervec[1] = "KERBEROS";
343             status = mr_query("add_member_to_list", 3, membervec,
344                                scream, NULL);
345             if (status != MR_SUCCESS)
346               com_err(whoami, status, "while adding member %s to %s",
347                       memberstruct->name, listname);
348         }
349     }
350
351     /* Process the delete list */
352     while (sq_get_data(dellist, &memberstruct)) {
353         membervec[0] = listname;
354         membervec[2] = memberstruct->name;
355         if (verbose) {
356             printf("Deleting member ");
357             show_list_member(memberstruct);
358         }
359         switch (memberstruct->type) {
360         case M_ANY:
361         case M_USER:
362             membervec[1] = "USER";
363             status = mr_query("delete_member_from_list", 3, membervec,
364                                scream, NULL);
365             if (status == MR_SUCCESS)
366               break;
367             else if ((status != MR_USER && status != MR_NO_MATCH) ||
368                      memberstruct->type != M_ANY) {
369                 com_err(whoami, status, "while deleting member %s from %s",
370                         memberstruct->name, listname);
371                 break;
372             }
373         case M_LIST:
374             membervec[1] = "LIST";
375             status = mr_query("delete_member_from_list", 3, membervec,
376                                scream, NULL);
377             if (status == MR_SUCCESS)
378               break;
379             else if ((status != MR_LIST && status != MR_NO_MATCH) ||
380                      memberstruct->type != M_ANY) {
381                 if (status == MR_PERM && memberstruct->type == M_ANY)  {
382                   /* M_ANY means we've fallen through from the user case
383                    * The fact that we didn't get MR_PERM there indicates
384                    * that we had permission to remove the specified member 
385                    * from the list if it is a user, but not a list.  This is 
386                    * if we are the member in question.  Since we exist as a user
387                    * we must have gotten the MR_NO_MATCH error, so we will
388                    * return that, since it will be less confusing.  However,
389                    * This will generate the wrongerror if the user was trying
390                    * to remove the list with his/her username from a list they
391                    * don't administrate, without explicitly specifying "list:".
392                    */
393                   status = MR_NO_MATCH;
394                 }
395                 com_err(whoami, status, "while deleting member %s from %s",
396                         memberstruct->name, listname);
397                 break;
398             }
399         case M_STRING:
400             membervec[1] = "STRING";
401             status = mr_query("delete_member_from_list", 3, membervec,
402                                scream, NULL);
403             if (status == MR_STRING && memberstruct->type == M_ANY) {
404               com_err(whoami, 0, " Unable to find member %s to delete from %s",
405                       memberstruct->name, listname);
406               if (!strcmp(membervec[0], getenv("USER"))) {
407                 fprintf(stderr, "(If you were trying to remove yourself from the list \"%s\",\n", membervec[2]);
408                 fprintf(stderr, "the correct command is \"blanche %s -d %s\".)\n",
409                         membervec[2], membervec[0]);
410               }
411             } else if (status != MR_SUCCESS)
412               com_err(whoami, status, "while deleting member %s from %s",
413                       memberstruct->name, listname);
414             break;
415         case M_KERBEROS:
416             membervec[1] = "KERBEROS";
417             status = mr_query("delete_member_from_list", 3, membervec,
418                                scream, NULL);
419             if (status != MR_SUCCESS)
420               com_err(whoami, status, "while deleting member %s from %s",
421                       memberstruct->name, listname);
422         }
423     }
424
425     /* Display the members of the list now, if requested */
426     if (memberflg) {
427         if (recursflg)
428           recursive_display_list_members();
429         else {
430             status = mr_query("get_members_of_list", 1, &listname,
431                                get_list_members, (char *)memberlist);
432             if (status)
433               com_err(whoami, status, "while getting members of list %s",
434                       listname);
435             while (sq_get_data(memberlist, &memberstruct))
436               show_list_member(memberstruct);
437         }
438     }
439
440     /* We're done! */
441     mr_disconnect();
442     exit(0);
443 }
444
445 usage(argv)
446 char **argv;
447 {
448     fprintf(stderr, "Usage: %s listname [options]\n",argv[0]);
449     fprintf(stderr, "Options are\n");
450     fprintf(stderr, "   -v | -verbose\n");
451     fprintf(stderr, "   -m | -members\n");
452     fprintf(stderr, "   -u | -users\n");
453     fprintf(stderr, "   -l | -lists\n");
454     fprintf(stderr, "   -s | -strings\n");
455     fprintf(stderr, "   -k | -kerberos\n");
456     fprintf(stderr, "   -i | -info\n");
457     fprintf(stderr, "   -r | -recursive\n");
458     fprintf(stderr, "   -a | -add member\n");
459     fprintf(stderr, "   -d | -delete member\n");
460     fprintf(stderr, "   -al | -addlist filename\n");
461     fprintf(stderr, "   -dl | -deletelist filename\n");
462     fprintf(stderr, "   -f | -file filename\n");
463     fprintf(stderr, "   -n | -noauth\n");
464     fprintf(stderr, "   -db | -database host[:port]\n");
465     fprintf(stderr, "   -D | -debug\n");
466     exit(1);
467 }
468
469
470 /* Display the members stored in the queue */
471
472 show_list_member(memberstruct)
473 struct member *memberstruct;
474 {
475     char *s = "";
476
477     switch (memberstruct->type) {
478     case M_USER:
479         if (!showusers)
480           return;
481         s = "USER";
482         break;
483     case M_LIST:
484         if (!showlists)
485           return;
486         s = "LIST";
487         break;
488     case M_STRING:
489         if (!showstrings)
490           return;
491         s = "STRING";
492         break;
493     case M_KERBEROS:
494         if (!showkerberos)
495           return;
496         s = "KERBEROS";
497         break;
498     case M_ANY:
499         printf("%s\n", memberstruct->name);
500         return;
501     }
502
503     if (verbose)
504       printf("%s:%s\n", s, memberstruct->name);
505     else {
506         if (memberstruct->type == M_LIST)
507           printf("LIST:%s\n", memberstruct->name);
508         else if (memberstruct->type == M_KERBEROS)
509           printf("KERBEROS:%s\n", memberstruct->name);
510         else if (memberstruct->type == M_STRING &&
511                  !strchr(memberstruct->name, '@'))
512           printf("STRING:%s\n", memberstruct->name);
513         else
514           printf("%s\n", memberstruct->name);
515     }
516 }
517
518
519 /* Show the retrieved information about a list */
520
521 show_list_info(argc, argv, hint)
522 int argc;
523 char **argv;
524 int hint;
525 {
526     printf("List: %s\n", argv[0]);
527     printf("Description: %s\n", argv[9]);
528     printf("Flags: %s, %s, and %s\n",
529            atoi(argv[1]) ? "active" : "inactive",
530            atoi(argv[2]) ? "public" : "private",
531            atoi(argv[3]) ? "hidden" : "visible");
532     printf("%s is %sa maillist and is %sa group", argv[0],
533            atoi(argv[4]) ? "" : "not ",
534            atoi(argv[5]) ? "" : "not ");
535     if (atoi(argv[5]))
536       printf(" with GID %d\n", atoi(argv[6]));
537     else
538       printf("\n");
539     printf("Owner: %s %s\n", argv[7], argv[8]);
540     printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
541     return(MR_CONT);
542 }
543
544
545 /* Show the retrieve list member count */
546
547 show_list_count(argc, argv, hint)
548 int argc;
549 char **argv;
550 int hint;
551 {
552     printf("Members: %s\n", argv[0]);
553 }
554
555
556 /* Recursively find all of the members of listname, and then display them */
557
558 recursive_display_list_members()
559 {
560     int status, count, savecount;
561     struct save_queue *lists, *members;
562     struct member *m, *m1, *data;
563
564     lists = sq_create();
565     members = sq_create();
566     m = (struct member *) malloc(sizeof(struct member));
567     m->type = M_LIST;
568     m->name = listname;
569     sq_save_data(lists, m);
570
571     while (sq_get_data(lists, &m)) {
572         sq_destroy(memberlist);
573         memberlist = sq_create();
574         if (debugflg)
575           fprintf(stderr, "Fetching members of %s\n", m->name);
576         status = mr_query("get_members_of_list", 1, &(m->name),
577                            get_list_members, (char *)memberlist);
578         if (status)
579           com_err(whoami, status, "while getting members of list %s", m->name);
580         while (sq_get_data(memberlist, &m1)) {
581             if (m1->type == M_LIST)
582               unique_add_member(lists, m1);
583             else
584               unique_add_member(members, m1);
585         }
586     }
587     savecount = count = sq_count_elts(members);
588     data = (struct member *) malloc(count * sizeof(struct member));
589     count = 0;
590     while (sq_get_data(members, &m))
591       memcpy(&data[count++], m, sizeof(struct member));
592     qsort(data, count, sizeof(struct member), membercmp);
593     for (count = 0; count < savecount; count++) {
594         show_list_member(&data[count]);
595     }
596 }
597
598
599 /* add a struct member to a queue if that member isn't already there. */
600
601 unique_add_member(q, m)
602 struct save_queue  *q;
603 struct member *m;
604 {
605     struct save_queue *qp;
606
607     for (qp = q->q_next; qp != q; qp = qp->q_next) {
608         if (!membercmp(qp->q_data, m))
609           return;
610     }
611     sq_save_data(q, m);
612 }
613
614
615 /* Collect the retrieved members of the list */
616
617 get_list_members(argc, argv, q)
618 int argc;
619 char **argv;
620 struct save_queue *q;
621 {
622     struct member *m;
623
624     m = (struct member *) malloc(sizeof(struct member));
625     switch (argv[0][0]) {
626     case 'U':
627         m->type = M_USER;
628         break;
629     case 'L':
630         m->type = M_LIST;
631         break;
632     case 'S':
633         m->type = M_STRING;
634         break;
635     case 'K':
636         m->type = M_KERBEROS;
637         break;
638     }
639     m->name = strsave(argv[1]);
640     sq_save_data(q, m);
641     return(MR_CONT);
642 }
643
644
645 /* Called only if a query returns a value that we weren't expecting */
646
647 scream()
648 {
649     fprintf(stderr, "Programmer botch\n");
650     exit(3);
651 }
652
653
654 /* Open file, parse members from file, and put them on the specified queue */
655 get_members_from_file(filename, queue)
656 char *filename;
657 struct save_queue *queue;
658 {
659     FILE *in;
660     char buf[BUFSIZ];
661     struct member *memberstruct;
662
663     if (!strcmp(filename, "-"))
664       in = stdin;
665     else {
666         in = fopen(filename, "r");
667         if (!in) {
668             com_err(whoami, errno, "while opening %s for input", filename);
669             exit(2);
670         }
671     }
672
673     while (fgets(buf, BUFSIZ, in))
674       if (memberstruct = parse_member(buf))
675         sq_save_data(queue, memberstruct);
676     if (!feof(in))
677       com_err(whoami, errno, "while reading from %s", filename);
678 }
679
680
681 /* Collect the possible expansions of the alias MAILHUB */
682
683 int collect(argc, argv, list)
684 int argc;
685 char **argv;
686 char ***list;
687 {
688     int i;
689
690     for (i = 0; (*list)[i]; i++);
691     *list = (char **)realloc(*list, (i + 2) * sizeof(char *));
692     (*list)[i] = strsave(argv[2]);
693     (*list)[i+1] = NULL;
694     return(MR_CONT);
695 }
696
697
698 /* Parse a line of input, fetching a member.  NULL is returned if a member
699  * is not found.  ';' is a comment character.
700  */
701
702 struct member *parse_member(s)
703 register char *s;
704 {
705     register struct member *m;
706     char *p, *lastchar;
707
708     while (*s && isspace(*s))
709       s++;
710     lastchar = p = s;
711     while (*p && *p != '\n' && *p != ';')
712       if (isprint(*p) && !isspace(*p))
713         lastchar = p++;
714       else
715         p++;
716     lastchar++;
717     *lastchar = 0;
718     if (p == s || strlen(s) == 0)
719       return(NULL);
720
721     if ((m = (struct member *) malloc(sizeof(struct member))) == NULL)
722       return(NULL);
723
724     if (p = strchr(s, ':')) {
725         *p = 0;
726         m->name = ++p;
727         if (!strcasecmp("user", s))
728           m->type = M_USER;
729         else if (!strcasecmp("list", s))
730           m->type = M_LIST;
731         else if (!strcasecmp("string", s))
732           m->type = M_STRING;
733         else if (!strcasecmp("kerberos", s))
734           m->type = M_KERBEROS;
735         else {
736             m->type = M_STRING;
737             *(--p) = ':';
738             m->name = s;
739         }
740         m->name = strsave(m->name);
741     } else {
742         m->name = strsave(s);
743         if (strchr(s, '@') || strchr(s, '!') || strchr(s, '%') || strchr(s, ' '))
744           m->type = M_STRING;
745         else
746           m->type = M_ANY;
747     }
748     return(m);
749 }
750
751
752   /* 
753    * This routine two compares members by the following rules:
754    * 1.  A USER is less than a LIST
755    * 2.  A LIST is less than a STRING
756    * 3.  If two members are of the same type, the one alphabetically first
757    *     is less than the other
758    * It returs < 0 if the first member is less, 0 if they are identical, and
759    * > 0 if the second member is less (the first member is greater).
760    */
761
762 int membercmp(m1, m2)
763   struct member *m1, *m2;
764 {
765     if (m1->type == M_ANY || m2->type  == M_ANY || (m1->type == m2->type))
766         return(strcmp(m1->name, m2->name));
767     else
768         return(m1->type - m2->type);
769 }
770
771
772 sq_count_elts(q)
773 struct save_queue *q;
774 {
775     char  *foo;
776     int count;
777
778     count = 0;
779     while (sq_get_data(q, &foo))
780       count++;
781     return(count);
782 }
This page took 0.610621 seconds and 5 git commands to generate.