]> andersk Git - moira.git/blob - clients/blanche/blanche.c
Warn/help users who probably got the arguments in the wrong order:
[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, "Illegal character \"'\" in \"STRING:%s\", aborting blanche.\n",
249                         memberstruct->name);
250                 fprintf(stderr, "No changes were made.\n");
251                 exit(2);
252         }
253         /* canonicalize string if necessary */
254         if (memberstruct->type == M_STRING &&
255             (p = strchr(memberstruct->name, '@'))) {
256             char *host = canonicalize_hostname(strsave(++p));
257             static char **mailhubs = NULL;
258             char *argv[4];
259             int i, collect();
260
261             if (!mailhubs) {
262                 argv[0] = "mailhub";
263                 argv[1] = "TYPE";
264                 argv[2] = "*";
265                 mailhubs = (char **)malloc(sizeof(char *));
266                 mailhubs[0] = NULL;
267                 status = mr_query("get_alias", 3, argv, collect,
268                                    (char *)&mailhubs);
269                 if (status != MR_SUCCESS && status != MR_NO_MATCH) {
270                     com_err(whoami, status,
271                             " while reading list of MAILHUB servers");
272                     mailhubs[0] = NULL;
273                 }
274             }
275             for (i = 0; p = mailhubs[i]; i++) {
276                 if (!strcasecmp(p, host)) {
277                     host = strsave(memberstruct->name);
278                     *(strchr(memberstruct->name, '@')) = 0;
279                     memberstruct->type = M_ANY;
280                     fprintf(stderr, "Warning: \"STRING:%s\" converted to \"%s\" because it is a local name.\n",
281                             host, memberstruct->name);
282                     break;
283                 }
284             }
285             free(host);
286         }
287         /* now continue adding member */
288         membervec[0] = listname;
289         membervec[2] = memberstruct->name;
290         if (verbose) {
291             printf("Adding member ");
292             show_list_member(memberstruct);
293         }
294         switch (memberstruct->type) {
295         case M_ANY:
296         case M_USER:
297             membervec[1] = "USER";
298             status = mr_query("add_member_to_list", 3, membervec, scream, NULL);
299             if (status == MR_SUCCESS)
300               break;
301             else if (status != MR_USER || memberstruct->type != M_ANY) {
302                 com_err(whoami, status, "while adding member %s to %s",
303                         memberstruct->name, listname);
304                 break;
305             }
306         case M_LIST:
307             membervec[1] = "LIST";
308             status = mr_query("add_member_to_list", 3, membervec,
309                                scream, NULL);
310             if (status == MR_SUCCESS) {
311                 if (!strcmp(membervec[0], getenv("USER"))) {
312                     fprintf(stderr, "\nWARNING: \"LIST:%s\" was just added to list \"%s\".\n",
313                             membervec[2], membervec[0]);
314                     fprintf(stderr, "If you meant to add yourself to the list \"%s\", type:\n", membervec[2]);
315                     fprintf(stderr, "\tblanche %s -d %s\t(to undo this)\n",
316                             membervec[0], membervec[2]);
317                     fprintf(stderr, "\tblanche %s -a %s\t(to add yourself to that list)\n",
318                             membervec[2], membervec[0]);
319                 }
320                 break;
321             } else if (status != MR_LIST || memberstruct->type != M_ANY) {
322                 com_err(whoami, status, "while adding member %s to %s",
323                         memberstruct->name, listname);
324                 break;
325             }
326         case M_STRING:
327             membervec[1] = "STRING";
328             status = mr_query("add_member_to_list", 3, membervec,
329                                scream, NULL);
330             if (status != MR_SUCCESS)
331               com_err(whoami, status, "while adding member %s to %s",
332                       memberstruct->name, listname);
333             else if (!strchr(memberstruct->name, '@') &&
334                      !strchr(memberstruct->name, '!') &&
335                      !strchr(memberstruct->name, '%')) {
336                 fprintf(stderr, "\nWARNING: \"STRING:%s\" was just added to list \"%s\".\n",
337                         memberstruct->name, listname);
338                 fprintf(stderr, "\tIf %s is a mailing list, this may cause it to stop working.\n", listname);
339                 fprintf(stderr, "\tYou should consider removing \"STRING:%s\" from the list.\n", memberstruct->name);
340             }
341             break;
342         case M_KERBEROS:
343             membervec[1] = "KERBEROS";
344             status = mr_query("add_member_to_list", 3, membervec,
345                                scream, NULL);
346             if (status != MR_SUCCESS)
347               com_err(whoami, status, "while adding member %s to %s",
348                       memberstruct->name, listname);
349         }
350     }
351
352     /* Process the delete list */
353     while (sq_get_data(dellist, &memberstruct)) {
354         membervec[0] = listname;
355         membervec[2] = memberstruct->name;
356         if (verbose) {
357             printf("Deleting member ");
358             show_list_member(memberstruct);
359         }
360         switch (memberstruct->type) {
361         case M_ANY:
362         case M_USER:
363             membervec[1] = "USER";
364             status = mr_query("delete_member_from_list", 3, membervec,
365                                scream, NULL);
366             if (status == MR_SUCCESS)
367               break;
368             else if ((status != MR_USER && status != MR_NO_MATCH) ||
369                      memberstruct->type != M_ANY) {
370                 com_err(whoami, status, "while deleting member %s from %s",
371                         memberstruct->name, listname);
372                 break;
373             }
374         case M_LIST:
375             membervec[1] = "LIST";
376             status = mr_query("delete_member_from_list", 3, membervec,
377                                scream, NULL);
378             if (status == MR_SUCCESS)
379               break;
380             else if ((status != MR_LIST && status != MR_NO_MATCH) ||
381                      memberstruct->type != M_ANY) {
382                 if (status == MR_PERM && memberstruct->type == M_ANY)  {
383                   /* M_ANY means we've fallen through from the user case
384                    * The fact that we didn't get MR_PERM there indicates
385                    * that we had permission to remove the specified member 
386                    * from the list if it is a user, but not a list.  This is 
387                    * if we are the member in question.  Since we exist as a user
388                    * we must have gotten the MR_NO_MATCH error, so we will
389                    * return that, since it will be less confusing.  However,
390                    * This will generate the wrongerror if the user was trying
391                    * to remove the list with his/her username from a list they
392                    * don't administrate, without explicitly specifying "list:".
393                    */
394                   status = MR_NO_MATCH;
395                 }
396                 com_err(whoami, status, "while deleting member %s from %s",
397                         memberstruct->name, listname);
398                 break;
399             }
400         case M_STRING:
401             membervec[1] = "STRING";
402             status = mr_query("delete_member_from_list", 3, membervec,
403                                scream, NULL);
404             if (status == MR_STRING && memberstruct->type == M_ANY) {
405               com_err(whoami, 0, " Unable to find member %s to delete from %s",
406                       memberstruct->name, listname);
407               if (!strcmp(membervec[0], getenv("USER"))) {
408                 fprintf(stderr, "(If you were trying to remove yourself from the list \"%s\",\n", membervec[2]);
409                 fprintf(stderr, "the correct command is \"blanche %s -d %s\".)\n",
410                         membervec[2], membervec[0]);
411               }
412             } else if (status != MR_SUCCESS)
413               com_err(whoami, status, "while deleting member %s from %s",
414                       memberstruct->name, listname);
415             break;
416         case M_KERBEROS:
417             membervec[1] = "KERBEROS";
418             status = mr_query("delete_member_from_list", 3, membervec,
419                                scream, NULL);
420             if (status != MR_SUCCESS)
421               com_err(whoami, status, "while deleting member %s from %s",
422                       memberstruct->name, listname);
423         }
424     }
425
426     /* Display the members of the list now, if requested */
427     if (memberflg) {
428         if (recursflg)
429           recursive_display_list_members();
430         else {
431             status = mr_query("get_members_of_list", 1, &listname,
432                                get_list_members, (char *)memberlist);
433             if (status)
434               com_err(whoami, status, "while getting members of list %s",
435                       listname);
436             while (sq_get_data(memberlist, &memberstruct))
437               show_list_member(memberstruct);
438         }
439     }
440
441     /* We're done! */
442     mr_disconnect();
443     exit(0);
444 }
445
446 usage(argv)
447 char **argv;
448 {
449     fprintf(stderr, "Usage: %s listname [options]\n",argv[0]);
450     fprintf(stderr, "Options are\n");
451     fprintf(stderr, "   -v | -verbose\n");
452     fprintf(stderr, "   -m | -members\n");
453     fprintf(stderr, "   -u | -users\n");
454     fprintf(stderr, "   -l | -lists\n");
455     fprintf(stderr, "   -s | -strings\n");
456     fprintf(stderr, "   -k | -kerberos\n");
457     fprintf(stderr, "   -i | -info\n");
458     fprintf(stderr, "   -r | -recursive\n");
459     fprintf(stderr, "   -a | -add member\n");
460     fprintf(stderr, "   -d | -delete member\n");
461     fprintf(stderr, "   -al | -addlist filename\n");
462     fprintf(stderr, "   -dl | -deletelist filename\n");
463     fprintf(stderr, "   -f | -file filename\n");
464     fprintf(stderr, "   -n | -noauth\n");
465     fprintf(stderr, "   -db | -server host[:port]\n");
466     fprintf(stderr, "   -D | -debug\n");
467     exit(1);
468 }
469
470
471 /* Display the members stored in the queue */
472
473 show_list_member(memberstruct)
474 struct member *memberstruct;
475 {
476     char *s = "";
477
478     switch (memberstruct->type) {
479     case M_USER:
480         if (!showusers)
481           return;
482         s = "USER";
483         break;
484     case M_LIST:
485         if (!showlists)
486           return;
487         s = "LIST";
488         break;
489     case M_STRING:
490         if (!showstrings)
491           return;
492         s = "STRING";
493         break;
494     case M_KERBEROS:
495         if (!showkerberos)
496           return;
497         s = "KERBEROS";
498         break;
499     case M_ANY:
500         printf("%s\n", memberstruct->name);
501         return;
502     }
503
504     if (verbose)
505       printf("%s:%s\n", s, memberstruct->name);
506     else {
507         if (memberstruct->type == M_LIST)
508           printf("LIST:%s\n", memberstruct->name);
509         else if (memberstruct->type == M_KERBEROS)
510           printf("KERBEROS:%s\n", memberstruct->name);
511         else if (memberstruct->type == M_STRING &&
512                  !strchr(memberstruct->name, '@'))
513           printf("STRING:%s\n", memberstruct->name);
514         else
515           printf("%s\n", memberstruct->name);
516     }
517 }
518
519
520 /* Show the retrieved information about a list */
521
522 show_list_info(argc, argv, hint)
523 int argc;
524 char **argv;
525 int hint;
526 {
527     printf("List: %s\n", argv[0]);
528     printf("Description: %s\n", argv[9]);
529     printf("Flags: %s, %s, and %s\n",
530            atoi(argv[1]) ? "active" : "inactive",
531            atoi(argv[2]) ? "public" : "private",
532            atoi(argv[3]) ? "hidden" : "visible");
533     printf("%s is %sa maillist and is %sa group", argv[0],
534            atoi(argv[4]) ? "" : "not ",
535            atoi(argv[5]) ? "" : "not ");
536     if (atoi(argv[5]))
537       printf(" with GID %d\n", atoi(argv[6]));
538     else
539       printf("\n");
540     printf("Owner: %s %s\n", argv[7], argv[8]);
541     printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
542     return(MR_CONT);
543 }
544
545
546 /* Show the retrieve list member count */
547
548 show_list_count(argc, argv, hint)
549 int argc;
550 char **argv;
551 int hint;
552 {
553     printf("Members: %s\n", argv[0]);
554 }
555
556
557 /* Recursively find all of the members of listname, and then display them */
558
559 recursive_display_list_members()
560 {
561     int status, count, savecount;
562     struct save_queue *lists, *members;
563     struct member *m, *m1, *data;
564
565     lists = sq_create();
566     members = sq_create();
567     m = (struct member *) malloc(sizeof(struct member));
568     m->type = M_LIST;
569     m->name = listname;
570     sq_save_data(lists, m);
571
572     while (sq_get_data(lists, &m)) {
573         sq_destroy(memberlist);
574         memberlist = sq_create();
575         if (debugflg)
576           fprintf(stderr, "Fetching members of %s\n", m->name);
577         status = mr_query("get_members_of_list", 1, &(m->name),
578                            get_list_members, (char *)memberlist);
579         if (status)
580           com_err(whoami, status, "while getting members of list %s", m->name);
581         while (sq_get_data(memberlist, &m1)) {
582             if (m1->type == M_LIST)
583               unique_add_member(lists, m1);
584             else
585               unique_add_member(members, m1);
586         }
587     }
588     savecount = count = sq_count_elts(members);
589     data = (struct member *) malloc(count * sizeof(struct member));
590     count = 0;
591     while (sq_get_data(members, &m))
592       memcpy(&data[count++], m, sizeof(struct member));
593     qsort(data, count, sizeof(struct member), membercmp);
594     for (count = 0; count < savecount; count++) {
595         show_list_member(&data[count]);
596     }
597 }
598
599
600 /* add a struct member to a queue if that member isn't already there. */
601
602 unique_add_member(q, m)
603 struct save_queue  *q;
604 struct member *m;
605 {
606     struct save_queue *qp;
607
608     for (qp = q->q_next; qp != q; qp = qp->q_next) {
609         if (!membercmp(qp->q_data, m))
610           return;
611     }
612     sq_save_data(q, m);
613 }
614
615
616 /* Collect the retrieved members of the list */
617
618 get_list_members(argc, argv, q)
619 int argc;
620 char **argv;
621 struct save_queue *q;
622 {
623     struct member *m;
624
625     m = (struct member *) malloc(sizeof(struct member));
626     switch (argv[0][0]) {
627     case 'U':
628         m->type = M_USER;
629         break;
630     case 'L':
631         m->type = M_LIST;
632         break;
633     case 'S':
634         m->type = M_STRING;
635         break;
636     case 'K':
637         m->type = M_KERBEROS;
638         break;
639     }
640     m->name = strsave(argv[1]);
641     sq_save_data(q, m);
642     return(MR_CONT);
643 }
644
645
646 /* Called only if a query returns a value that we weren't expecting */
647
648 scream()
649 {
650     fprintf(stderr, "Programmer botch\n");
651     exit(3);
652 }
653
654
655 /* Open file, parse members from file, and put them on the specified queue */
656 get_members_from_file(filename, queue)
657 char *filename;
658 struct save_queue *queue;
659 {
660     FILE *in;
661     char buf[BUFSIZ];
662     struct member *memberstruct;
663
664     if (!strcmp(filename, "-"))
665       in = stdin;
666     else {
667         in = fopen(filename, "r");
668         if (!in) {
669             com_err(whoami, errno, "while opening %s for input", filename);
670             exit(2);
671         }
672     }
673
674     while (fgets(buf, BUFSIZ, in))
675       if (memberstruct = parse_member(buf))
676         sq_save_data(queue, memberstruct);
677     if (!feof(in))
678       com_err(whoami, errno, "while reading from %s", filename);
679 }
680
681
682 /* Collect the possible expansions of the alias MAILHUB */
683
684 int collect(argc, argv, list)
685 int argc;
686 char **argv;
687 char ***list;
688 {
689     int i;
690
691     for (i = 0; (*list)[i]; i++);
692     *list = (char **)realloc(*list, (i + 2) * sizeof(char *));
693     (*list)[i] = strsave(argv[2]);
694     (*list)[i+1] = NULL;
695     return(MR_CONT);
696 }
697
698
699 /* Parse a line of input, fetching a member.  NULL is returned if a member
700  * is not found.  ';' is a comment character.
701  */
702
703 struct member *parse_member(s)
704 register char *s;
705 {
706     register struct member *m;
707     char *p, *lastchar;
708
709     while (*s && isspace(*s))
710       s++;
711     lastchar = p = s;
712     while (*p && *p != '\n' && *p != ';')
713       if (isprint(*p) && !isspace(*p))
714         lastchar = p++;
715       else
716         p++;
717     lastchar++;
718     *lastchar = 0;
719     if (p == s || strlen(s) == 0)
720       return(NULL);
721
722     if ((m = (struct member *) malloc(sizeof(struct member))) == NULL)
723       return(NULL);
724
725     if (p = strchr(s, ':')) {
726         *p = 0;
727         m->name = ++p;
728         if (!strcasecmp("user", s))
729           m->type = M_USER;
730         else if (!strcasecmp("list", s))
731           m->type = M_LIST;
732         else if (!strcasecmp("string", s))
733           m->type = M_STRING;
734         else if (!strcasecmp("kerberos", s))
735           m->type = M_KERBEROS;
736         else {
737             m->type = M_STRING;
738             *(--p) = ':';
739             m->name = s;
740         }
741         m->name = strsave(m->name);
742     } else {
743         m->name = strsave(s);
744         if (strchr(s, '@') || strchr(s, '!') || strchr(s, '%') || strchr(s, ' '))
745           m->type = M_STRING;
746         else
747           m->type = M_ANY;
748     }
749     return(m);
750 }
751
752
753   /* 
754    * This routine two compares members by the following rules:
755    * 1.  A USER is less than a LIST
756    * 2.  A LIST is less than a STRING
757    * 3.  If two members are of the same type, the one alphabetically first
758    *     is less than the other
759    * It returs < 0 if the first member is less, 0 if they are identical, and
760    * > 0 if the second member is less (the first member is greater).
761    */
762
763 int membercmp(m1, m2)
764   struct member *m1, *m2;
765 {
766     if (m1->type == M_ANY || m2->type  == M_ANY || (m1->type == m2->type))
767         return(strcmp(m1->name, m2->name));
768     else
769         return(m1->type - m2->type);
770 }
771
772
773 sq_count_elts(q)
774 struct save_queue *q;
775 {
776     char  *foo;
777     int count;
778
779     count = 0;
780     while (sq_get_data(q, &foo))
781       count++;
782     return(count);
783 }
This page took 0.098716 seconds and 5 git commands to generate.