]> andersk Git - moira.git/blob - server/qrtn.qc
do better database locking
[moira.git] / server / qrtn.qc
1 /*
2  *      $Source$
3  *      $Author$
4  *      $Header$
5  *
6  *      Copyright (C) 1987, 1988 by the Massachusetts Institute of Technology
7  *      For copying and distribution information, please see the file
8  *      <mit-copyright.h>.
9  * 
10  */
11
12 #ifndef lint
13 static char *rcsid_qrtn_qc = "$Header$";
14 #endif lint
15
16 #include <mit-copyright.h>
17 #include "query.h"
18 #include "sms_server.h"
19
20 char *Argv[16];
21
22 int ingres_errno = 0;
23 int sms_errcode = 0;
24 ## int query_timeout = 30;
25 extern char *whoami;
26 extern FILE *journal;
27
28 #define INGRES_BAD_INT 4111
29 #define INGRES_BAD_DATE 4302
30 #define INGRES_DEADLOCK 4700
31 #define INGRES_TIMEOUT 4702
32
33 /*
34  * ingerr: (supposedly) called when Ingres indicates an error.
35  * I have not yet been able to get this to work to intercept a
36  * database open error.
37  */
38
39 static int ingerr(num)
40     int *num;
41 {
42     ingres_errno = *num;
43
44     switch (*num) {
45     case INGRES_BAD_INT:
46         sms_errcode = SMS_INTEGER;
47         break;
48     case INGRES_BAD_DATE:
49         sms_errcode = SMS_DATE;
50         break;
51     case INGRES_DEADLOCK:
52         sms_errcode = SMS_DEADLOCK;
53         com_err(whoami, 0, "INGRES deadlock detected");
54         break;
55     case INGRES_TIMEOUT:
56         sms_errcode = SMS_BUSY;
57         com_err(whoami, 0, "timed out getting lock");
58         break;
59     default:
60         sms_errcode = SMS_INGRES_ERR;
61         com_err(whoami, SMS_INGRES_ERR, " code %d\n", *num);
62         critical_alert("SMS", "SMS server encountered INGRES ERROR %d", *num);
63         return (*num);
64     }
65     return (0);
66 }
67
68 int sms_open_database()
69 {
70     register int i;
71     char *malloc();
72
73     /* initialize local argv */
74     for (i = 0; i < 16; i++)
75         Argv[i] = malloc(ARGLEN);
76
77     IIseterr(ingerr);
78         
79     ingres_errno = 0;
80         
81     /* open the database */
82 ##  ingres sms
83 ##  set lockmode session where level = table, timeout = query_timeout
84     return ingres_errno;
85 }
86
87 int sms_close_database()
88 {
89 ##  exit
90 }
91
92 sms_check_access(cl, name, argc, argv_ro)
93     client *cl;
94     char *name;
95     int argc;
96     char *argv_ro[];
97 {
98     struct query *q;
99     struct query *get_query_by_name();
100
101     ingres_errno = 0;
102
103     q = get_query_by_name(name, cl->args->sms_version_no);
104     if (q == (struct query *)0)
105         return(SMS_NO_HANDLE);
106
107     return(sms_verify_query(cl, q, argc, argv_ro));
108 }
109
110 sms_process_query(cl, name, argc, argv_ro, action, actarg)
111     client *cl;
112     char *name;
113     int argc;
114     char *argv_ro[];
115     int (*action)();
116     char *actarg;
117 {
118     register struct query *q;
119     register int status;
120     register struct validate *v;
121     char qual[256];
122     char sort[32];
123     char *pqual;
124     char *psort;
125 ##  char *table;
126     struct save_queue *sq;
127     struct query *get_query_by_name();
128     int sq_save_args();
129     struct save_queue *sq_create();
130     char *build_sort();
131
132     ingres_errno = 0;
133
134     /* list queries command */
135     if (!strcmp(name, "_list_queries")) {
136         list_queries(cl->args->sms_version_no, action, actarg);
137         return(SMS_SUCCESS);
138     }
139
140     /* help query command */
141     if (!strcmp(name, "_help")) {
142         if (argc < 1)
143             return(SMS_ARGS);
144         q = get_query_by_name(argv_ro[0], cl->args->sms_version_no);
145         if (q == (struct query *)0) return(SMS_NO_HANDLE);
146         help_query(q, action, actarg);
147         return(SMS_SUCCESS);
148     }
149
150     /* get query structure, return error if named query does not exist */
151     q = get_query_by_name(name, cl->args->sms_version_no);
152     if (q == (struct query *)0) return(SMS_NO_HANDLE);
153     v = q->validate;
154
155     if (q->type != RETRIEVE) {
156 ##      set lockmode session where readlock = exclusive
157 ##      begin transaction
158     }
159
160     /* setup argument vector, verify access and arguments */
161     if ((status = sms_verify_query(cl, q, argc, argv_ro)) != SMS_SUCCESS)
162         goto out;
163
164     /* perform any special query pre-processing */
165     if (v && v->pre_rtn) {
166         status = (*v->pre_rtn)(q, Argv, cl, 0);
167         if (status != SMS_SUCCESS)
168             goto out;
169     }
170
171     switch (q->type) {
172     case RETRIEVE:
173         /* for queries that do not permit wildcarding, check if row
174            uniquely exists */
175         if (v && v->field) {
176             status = validate_row(q, Argv, v);
177             if (status != SMS_EXISTS) break;
178         }
179
180         /* build "where" clause if needed */
181         if (q->qual) {
182             build_qual(q->qual, q->argc, Argv, qual);
183             pqual = qual;
184         } else {
185             pqual = 0;
186         }
187
188         /* build "sort" clause if needed */
189         if (v && v->valobj) {
190             psort = build_sort(v, sort);
191         } else {
192             psort = 0;
193         }
194
195         /* if there is a followup routine, then we must save the results */
196         /* of the first query for use by the followup routine */
197         /* if q->rvar = NULL, perform post_rtn only */
198         if (q->rvar) {
199             if (v && v->post_rtn) {
200                 sq = sq_create();
201                 status = do_retrieve(q, pqual, psort, sq_save_args, sq);
202                 if (status != SMS_SUCCESS) {
203                     sq_destroy(sq);
204                     break;
205                 }
206                 status = (*v->post_rtn)(q, sq, v, action, actarg, cl);
207             } else {
208                 /* normal retrieve */
209                 status = do_retrieve(q, pqual, psort, action, actarg);
210             }
211             if (status != SMS_SUCCESS) break;
212         } else {
213             status = (*v->post_rtn)(q, Argv, cl, action, actarg);
214         }
215
216         break;
217
218     case UPDATE:
219         /* see if row already exists */
220         if (v->field) {
221             status = validate_row(q, Argv, v);
222             if (status != SMS_EXISTS) break;
223         }
224
225         /* build "where" clause and perform update */
226         /* if q->rvar = NULL, perform post_rtn only */
227         if (q->rvar) {
228             build_qual(q->qual, q->argc, Argv, qual);
229             status = do_update(q, &Argv[q->argc], qual, action, actarg);
230             if (status != SMS_SUCCESS) break;
231             table = q->rtable;
232             if (strcmp(q->shortname, "sshi") && strcmp(q->shortname, "ssif")) {
233 ##              repeat replace tblstats (updates = tblstats.updates + 1,
234 ##                                       modtime = "now")
235 ##                  where tblstats.#table = @table
236             }
237         }
238
239         /* execute followup routine (if any) */
240         if (v->post_rtn) status = (*v->post_rtn)(q, Argv, cl);
241
242         break;
243
244     case APPEND:
245         /* see if row already exists */
246         if (v->field) {
247             status = validate_row(q, Argv, v);
248             if (status != SMS_NO_MATCH) break;
249         }
250
251         /* increment id number if necessary */
252         if (v->object_id) {
253             status = set_next_object_id(v->object_id, q->rtable);
254             if (status != SMS_SUCCESS) break;
255         }
256
257         /* build "where" clause if needed */
258         if (q->qual) {
259             build_qual(q->qual, q->argc, Argv, qual);
260             pqual = qual;
261         } else {
262             pqual = 0;
263         }
264
265         /* perform the append */
266         /* if q->rvar = NULL, perform post_rtn only */
267         if (q->rvar) {
268             status = do_append(q, &Argv[q->argc], pqual, action, actarg);
269             if (status != SMS_SUCCESS) break;
270             table = q->rtable;
271 ##          repeat replace tblstats (appends = tblstats.appends + 1,
272 ##                                   modtime = "now")
273 ##              where tblstats.#table = @table
274         }
275         
276         /* execute followup routine */
277         if (v->post_rtn) status = (*v->post_rtn)(q, Argv, cl);
278         break;
279
280     case DELETE:
281         /* see if row already exists */
282         if (v->field) {
283             status = validate_row(q, Argv, v);
284             if (status != SMS_EXISTS) break;
285         }
286
287         /* build "where" clause and perform delete */
288         /* if q->rvar = NULL, perform post_rtn only */
289         if (q->rvar) {
290             build_qual(q->qual, q->argc, Argv, qual);
291             status = do_delete(q, qual, action, actarg);
292             if (status != SMS_SUCCESS) break;
293             table = q->rtable;
294 ##          repeat replace tblstats (deletes = tblstats.deletes + 1,
295 ##                                   modtime = "now")
296 ##              where tblstats.#table = @table
297         }
298
299         /* execute followup routine */
300         if (v->post_rtn) status = (*v->post_rtn)(q, Argv, cl);
301         break;
302
303     }
304
305 out:
306     if (q->type != RETRIEVE) {
307         if (status == SMS_SUCCESS) {
308 ##          end transaction     /* commit to this */
309             if (journal) {
310                 char buf[1024], *bp;
311                 int i;
312                 extern time_t now;
313
314                 fprintf(journal, "%% %s %s %s",
315                         cl->clname, cl->entity, ctime(&now));
316                 fprintf(journal, "%s[%d] ", q->name, cl->args->sms_version_no);
317                 for (i = 0; i < argc; i++) {
318                     if (i != 0) {
319                         putc(' ', journal);
320                     }
321                     requote(buf, argv_ro[i], sizeof(buf));
322                     fputs(buf, journal);
323                 }
324                 putc('\n', journal);
325                 fflush(journal);
326             }
327         } else {
328             if (ingres_errno != INGRES_DEADLOCK) {
329 ##              abort           /* it never happened */
330             }
331         }
332 ##      set lockmode session where readlock = system
333     }
334
335     if (status == SMS_SUCCESS && ingres_errno != 0) {
336         critical_alert("SMS", "Server didn't notice INGRES ERROR %d",
337                        ingres_errno);
338         status = SMS_INTERNAL;
339     }
340
341     if (status != SMS_SUCCESS && log_flags & LOG_RES)
342         com_err(whoami, status, " (Query failed)");
343     /* until all the clients know about SMS_BUSY, convert this error
344      * to one they will recognize.
345      */
346     if (status == SMS_BUSY)
347       status = SMS_DEADLOCK;
348     return(status);
349 }
350
351 build_qual(fmt, argc, argv, qual)
352         char *fmt;
353         int argc;
354         char *argv[];
355         char *qual;
356 {
357     register char *c;
358     register int i;
359     char *args[4];
360     char *index();
361
362     c = fmt;
363     for (i = 0; i < argc; i++) {
364         c = index(c, '%');
365         if (c++ == (char *)0) return(SMS_ARGS);
366         if (*c == 's')
367             args[i] = argv[i];
368         else if (*c == 'd')
369             *(int *)&args[i] = *(int *)argv[i]; /* sigh */
370         else
371             return(SMS_INGRES_ERR);
372     }
373
374     switch (argc) {
375     case 0:
376         strcpy(qual, fmt);
377         break;
378
379     case 1:
380         sprintf(qual, fmt, args[0]);
381         break;
382
383     case 2:
384         sprintf(qual, fmt, args[0], args[1]);
385         break;
386
387     case 3:
388         sprintf(qual, fmt, args[0], args[1], args[2]);
389         break;
390
391     case 4:
392         sprintf(qual, fmt, args[0], args[1], args[2], args[3]);
393         break;
394     }
395     return(SMS_SUCCESS);
396 }
397
398 char *
399 build_sort(v, sort)
400     register struct validate *v;
401     char *sort;
402 {
403     register struct valobj *vo;
404     register int n;
405     char elem[16];
406
407     n = v->objcnt;
408     vo = v->valobj;
409     *sort = 0;
410
411     while (--n >= 0) {
412         if (vo->type == V_SORT) {
413             sprintf(elem, "RET_VAR%d", vo->index + 1);
414             if (*sort) strcat(sort, ", ");
415             strcat(sort, elem);
416         }
417         vo++;
418     }
419
420     return ((*sort) ? sort : 0);
421 }
422
423
424 /* Build arguement vector, verify query and arguments */
425
426 sms_verify_query(cl, q, argc, argv_ro)
427     client *cl;
428     struct query *q;
429     int argc;
430     char *argv_ro[];
431 {
432     register int argreq;
433     register int status;
434     register struct validate *v = q->validate;
435     register int i;
436     register int privileged = 0;
437     int len;
438
439     /* copy the arguments into a local argv that we can modify */
440     if (argc >= QMAXARGS)
441       return(SMS_ARGS);
442     for (i = 0; i < argc; i++) {
443         if ((len = strlen(argv_ro[i])) < ARGLEN)
444             strcpy(Argv[i], argv_ro[i]);
445         else
446             return(SMS_ARG_TOO_LONG);
447         if (Argv[i][len-1] == '\\')
448           return(SMS_BAD_CHAR);
449     }
450
451     /* check initial query access */
452     status = check_query_access(q, Argv, cl);
453     if (status != SMS_SUCCESS && status != SMS_PERM)
454         return(status);
455     if (status == SMS_SUCCESS)
456         privileged++;
457
458     /* check argument count */
459     argreq = q->argc;
460     if (q->type == UPDATE || q->type == APPEND) argreq += q->vcnt;
461     if (argc != argreq) return(SMS_ARGS);
462
463     /* validate arguments */
464     if (v && v->valobj) {
465         status = validate_fields(q, Argv, v->valobj, v->objcnt);
466         if (status != SMS_SUCCESS) return(status);
467     }
468
469     /* perform special query access check */
470     if (!privileged && v && v->acs_rtn) {
471         status = (*v->acs_rtn)(q, Argv, cl);
472         if (status != SMS_SUCCESS && status != SMS_PERM)
473             return(status);
474         if (status == SMS_SUCCESS)
475             privileged++;
476     }
477
478     return(privileged ? SMS_SUCCESS : SMS_PERM);
479 }
480
481 check_query_access(q, argv, cl)
482     struct query *q;
483     char *argv[];
484     client *cl;
485 ##{
486 ##  char *name;
487 ##  int acl_id;
488 ##  int exists;
489 ##  int rowcount;
490 ##  int errorno;
491 ##  static int def_uid;
492     int status;
493     int client_id;
494     char *client_type;
495
496     /* get query access control list */
497     name = q->shortname;
498 ##  repeat retrieve (acl_id = capacls.list_id) where capacls.tag = @name
499 ##  inquire_equel (rowcount = "rowcount", errorno = "errorno")
500     if (errorno != 0) return(SMS_INGRES_ERR);
501     if (rowcount == 0) return(SMS_PERM);
502
503     /* initialize default uid */
504     if (def_uid == 0) {
505 ##      retrieve (def_uid = users.users_id) where users.login = "default"
506     }
507
508     /* check for default access */
509 ##  range of m is members
510 ##  repeat retrieve (exists = any(m.#member_id where m.list_id = @acl_id and
511 ##                   m.member_type = "USER" and m.#member_id = def_uid))
512     if (exists) return(SMS_SUCCESS);
513
514     /* parse client name */
515     status = get_client(cl, &client_type, &client_id);
516     if (status != SMS_SUCCESS) return(status);
517
518     /* see if client is in the list (or any of its sub-lists) */
519     exists = find_member("LIST", acl_id, client_type, client_id, 0);
520     return ((exists) ? SMS_SUCCESS : SMS_PERM);
521 ##}
522
523 get_client(cl, client_type, client_id)
524     client *cl;
525     char **client_type;
526     int *client_id;
527 ##{
528     struct krbname *krb;
529 ##  int member_id;
530 ##  char *name;
531 ##  int rowcount;
532
533     if (cl->clname == NULL)
534         return SMS_PERM;
535     
536     /* for now ignore instances */
537     krb = &cl->kname;
538
539     /* if client is from local realm, get users_id */
540     if (!strcmp(krb->realm, krb_realm)) {
541         *client_id = cl->users_id;
542         *client_type = "USER";
543         return(SMS_SUCCESS);
544     }
545
546     /* otherwise use string_id */
547     name = cl->clname;
548 ##  repeat retrieve (member_id = strings.string_id) 
549 ##      where strings.string = @name
550         
551     /* make sure we found a users or string id */
552 ##  inquire_equel (rowcount = "rowcount")
553     if (rowcount == 0) return(SMS_PERM);
554
555     *client_type = "STRING";
556     *client_id = member_id;
557     return(SMS_SUCCESS);
558 ##}
559
560 ##find_member(list_type, list_id, member_type, member_id, sq)
561     char *list_type;
562 ##  int list_id;
563 ##  char *member_type;
564 ##  int member_id;
565     struct save_queue *sq;
566 ##{
567 ##  int exists;
568 ##  int sublist;
569     int child;
570     struct save_queue *sq_create();
571
572     if (!strcmp(strtrim(list_type), strtrim(member_type)) &&
573         list_id == member_id)
574         return(1);
575
576     /* see if client is a direct member of list */
577 ##  repeat retrieve (exists = any(m.#member_id where 
578 ##                                m.#list_id = @list_id and
579 ##                                m.#member_type = @member_type and 
580 ##                                m.#member_id = @member_id))
581     if (exists) return(1);
582
583     /* are there any sub-lists? */
584 ##  repeat retrieve (exists = any(m.#member_id where m.#list_id = @list_id and
585 ##                   m.#member_type = "LIST"))
586     if (!exists) return(0);
587
588     /* yes; now recurse through sublists */
589
590     /* create a save queue */
591     if (sq == (struct save_queue *)0) {
592         sq = sq_create();
593         child = 0;
594     } else {
595         child = 1;
596     }
597
598     /* save all sublist ids */
599 ##  range of m is members
600 ##  retrieve (sublist = m.#member_id) 
601 ##      where m.#list_id = list_id and m.#member_type = "LIST"
602 ##  {
603          sq_save_unique_data(sq, sublist);
604 ##  }
605
606     if (child) return(0);
607
608     /* at top-level, check sub-lists for client (breadth-first search) */
609     while (sq_get_data(sq, &sublist)) {
610         exists = find_member(list_type, sublist, member_type, member_id, sq);
611         if (exists) {
612             sq_destroy(sq);
613             return(1);
614         }
615     }
616     sq_destroy(sq);
617     return(0);
618 ##}
619
620
621 do_retrieve(q, pqual, psort, action, actarg)
622     register struct query *q;
623     char *pqual;
624     char *psort;
625     int (*action)();
626     char *actarg;
627 ##{
628 ##  char *rvar;
629 ##  char *rtable;
630 ##  char *cqual;
631 ##  char *csort;
632 ##  int rowcount;
633 ##  int errorno;
634     static char **vaddrs = (char **)NULL;
635
636     if (!vaddrs) {
637         register int i;
638
639         if ((vaddrs = (char **)malloc(sizeof(char *) * QMAXARGS)) == NULL) {
640             com_err(whoami, SMS_NO_MEM, "setting up static argv");
641             exit(1);
642         }
643         for (i = 0; i < QMAXARGS; i++) {
644             if ((vaddrs[i] = malloc(QMAXARGSIZE)) == NULL) {
645                 com_err(whoami, SMS_NO_MEM, "setting up static argv");
646                 exit(1);
647             }
648         }
649     }
650
651     if (q->rvar) {
652         rvar = q->rvar;
653         rtable = q->rtable;
654 ##      range of rvar is rtable
655     }
656
657     if (psort) {
658         csort = psort;
659         if (pqual) {
660             cqual = pqual;
661 ##          retrieve unique (param (q->tlist, vaddrs)) where cqual
662 ##                   sort by csort
663 ##          {
664                  (*action)(q->vcnt, vaddrs, actarg);
665 ##          }
666         } else {
667 ##          retrieve unique (param (q->tlist, vaddrs))
668 ##                   sort by csort
669 ##          {
670                  (*action)(q->vcnt, vaddrs, actarg);
671 ##          }
672         }
673
674     } else {
675         if (pqual) {
676             cqual = pqual;
677 ##          retrieve unique (param (q->tlist, vaddrs)) where cqual
678 ##          {
679                  (*action)(q->vcnt, vaddrs, actarg);
680 ##          }
681         } else {
682 ##          retrieve unique (param (q->tlist, vaddrs))
683 ##          {
684                  (*action)(q->vcnt, vaddrs, actarg);
685 ##          }
686         }
687     }
688
689 ##  inquire_equel (rowcount = "rowcount", errorno = "errorno")
690     if (errorno != 0) return(SMS_INGRES_ERR);
691     return ((rowcount == 0) ? SMS_NO_MATCH : SMS_SUCCESS);
692 ##}
693
694 do_update(q, argv, qual, action, actarg)
695     register struct query *q;
696     char *argv[];
697     char *qual;
698     int (*action)();
699     char *actarg;
700 ##{
701 ##  char *rvar;
702 ##  char *rtable;
703 ##  char *cqual;
704 ##  int errorno;
705       
706     rvar = q->rvar;
707     rtable = q->rtable;
708 ##  range of rvar is rtable
709
710     cqual = qual;
711 ##  replace rvar (param (q->tlist, argv))
712 ##  where cqual
713
714 ##  inquire_equel (errorno = "errorno")
715     if (errorno == INGRES_BAD_INT)
716         return(SMS_INTEGER);
717     else if (errorno != 0)
718         return(SMS_INGRES_ERR);
719     return(SMS_SUCCESS);
720 ##}
721
722 do_append(q, argv, pqual, action, actarg)
723     register struct query *q;
724     char *argv[];
725     char *pqual;
726     int (*action)();
727     char *actarg;
728 ##{
729 ##  char *rvar;
730 ##  char *rtable;
731 ##  char *cqual;
732 ##  int errorno;
733
734     rvar = q->rvar;
735     rtable = q->rtable;
736 ##  range of rvar is rtable
737
738     if (pqual) {
739         cqual = pqual;
740 ##      append to rtable (param (q->tlist, argv)) where cqual
741     } else {
742 ##      append to rtable (param (q->tlist, argv))
743     }
744
745 ##  inquire_equel (errorno = "errorno")
746     if (errorno == INGRES_BAD_INT)
747         return(SMS_INTEGER);
748     else if (errorno != 0)
749         return(SMS_INGRES_ERR);
750     return(SMS_SUCCESS);
751 ##}
752
753 do_delete(q, qual, action, actarg)
754     register struct query *q;
755     char *qual;
756     int (*action)();
757     char *actarg;
758 ##{
759 ##  char *rvar;
760 ##  char *rtable;
761 ##  char *cqual;
762 ##  int errorno;
763
764     rvar = q->rvar;
765     rtable = q->rtable;
766 ##  range of rvar is rtable
767
768     cqual = qual;
769 ##  delete rvar where cqual
770
771 ##  inquire_equel (errorno = "errorno")
772     if (errorno != 0) return(SMS_INGRES_ERR);
773     return(SMS_SUCCESS);
774 ##}
775
776
777 /**
778  ** set_next_object_id - set next object id in values table
779  **
780  ** Inputs: object - object name in values table and in objects
781  **         table - name of table objects are found in
782  **
783  ** - called before an APPEND operation to set the next object id to
784  **   be used for the new record to the next free value
785  **
786  **/
787
788 set_next_object_id(object, table)
789     char *object;
790     char *table;
791 ##{
792 ##  char *name, *tbl;
793 ##  int rowcount, exists, value;
794
795     name = object;
796     tbl = table;
797 ##  range of v is values
798 ##  repeat retrieve (value = v.#value) where v.#name = @name
799 ##  inquire_equel(rowcount = "rowcount")
800     if (rowcount != 1)
801         return(SMS_NO_ID);
802
803 ##  retrieve (exists = any(tbl.name where tbl.name = value))
804 ##  inquire_equel(rowcount = "rowcount")
805     if (rowcount != 1)
806         return(SMS_NO_ID);
807     while (exists) {
808         value++;
809         if (value > MAX_ID_VALUE)
810             value = MIN_ID_VALUE;
811 ##      retrieve (exists = any(tbl.name where tbl.name = value))
812     }
813
814     if (LOG_RES)
815         com_err(whoami, 0, "setting ID %s to %d", name, value);
816 ##  repeat replace v (#value = @value) where v.#name = @name
817     return(SMS_SUCCESS);
818 ##}
819
820
821 /* For now this just checks the argc's.  It should also see that there
822  * are no duplicate names.
823  */
824
825 sanity_check_queries()
826 {
827     register int i;
828     int maxv = 0, maxa = 0;
829     extern int QueryCount1, QueryCount2;
830     extern struct query Queries1[], Queries2[];
831 #define MAX(x,y) ((x) > (y) ? (x) : (y))
832
833     for (i = 0; i < QueryCount1; i++) {
834         maxv = MAX(maxv, Queries1[i].vcnt);
835         maxa = MAX(maxa, Queries1[i].argc);
836     }
837     for (i = 0; i < QueryCount2; i++) {
838         maxv = MAX(maxv, Queries2[i].vcnt);
839         maxa = MAX(maxa, Queries2[i].argc);
840     }
841     if (MAX(maxv, maxa) > QMAXARGS) {
842         com_err(whoami, 0, "A query has more args than QMAXARGS");
843         exit(1);
844     }
845 }
This page took 0.327229 seconds and 5 git commands to generate.