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