source: trunk/third/moira/server/increment.pc @ 25455

Revision 25455, 19.1 KB checked in by jdreed, 12 years ago (diff)
In moira: * Re-snapshot moira at r4073 to pick up new changes to clients; the eunice issue described in the previous entry is no longer relevant
Line 
1/* $Id: increment.pc 4068 2012-02-02 21:31:10Z zacheiss $
2 *
3 * Deal with incremental updates
4 *
5 * Copyright (C) 1989-1998 by the Massachusetts Institute of Technology
6 * For copying and distribution information, please see the file
7 * <mit-copyright.h>.
8 */
9
10#include <mit-copyright.h>
11#include "mr_server.h"
12#include "query.h"
13#include "qrtn.h"
14
15#include <signal.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <unistd.h>
20#include <sys/param.h>
21
22EXEC SQL INCLUDE sqlca;
23
24RCSID("$HeadURL: svn+ssh://svn.mit.edu/moira/trunk/moira/server/increment.pc $ $Id: increment.pc 4068 2012-02-02 21:31:10Z zacheiss $");
25
26extern char *whoami;
27extern char *table_name[];
28extern int num_tables;
29
30int inc_pid = 0;
31int inc_running = 0;
32time_t inc_started;
33
34#define MAXARGC 20
35
36EXEC SQL WHENEVER SQLERROR DO dbmserr();
37
38EXEC SQL BEGIN DECLARE SECTION;
39/* structures to save before args */
40static char *before[MAXARGC];
41static int beforec;
42static enum tables beforetable;
43
44/* structures to save after args */
45static char *after[MAXARGC];
46static int afterc;
47EXEC SQL END DECLARE SECTION;
48
49/* structures to save entire sets of incremental changes */
50struct save_queue *incremental_sq = NULL;
51struct save_queue *incremental_exec = NULL;
52struct iupdate {
53  char *table;
54  int beforec;
55  char **before;
56  int afterc;
57  char **after;
58  char *service;
59};
60
61void next_incremental(void);
62char **copy_argv(char **argv, int argc);
63void free_argv(char **argv, int argc);
64int table_num(char *table);
65
66void incremental_init(void)
67{
68  int i;
69
70  if (!incremental_sq)
71    incremental_sq = sq_create();
72  if (!incremental_exec)
73    incremental_exec = sq_create();
74
75  for (i = 0; i < MAXARGC; i++)
76    {
77      before[i] = xmalloc(MAX_FIELD_WIDTH);
78      after[i] = xmalloc(MAX_FIELD_WIDTH);
79    }
80}
81
82
83/* record the state of a table row before it is changed */
84
85void incremental_before(enum tables table, char *qual, char **argv)
86{
87  EXEC SQL BEGIN DECLARE SECTION;
88  int id;
89  EXEC SQL END DECLARE SECTION;
90
91  char *name, *name2;
92
93  beforetable = table;
94
95  switch (table)
96    {
97    case USERS_TABLE:
98      sprintf(stmt_buf, "SELECT u.login, u.unix_uid, u.shell, "
99              "u.winconsoleshell, u.last, u.first, u.middle, u.status, "
100              "u.clearid, u.type, u.users_id, u.winhomedir, u.winprofiledir, "
101              "u.potype FROM users u WHERE %s", qual);
102      dosql(before);
103      beforec = 14;
104      break;
105    case MACHINE_TABLE:
106      sprintf(stmt_buf, "SELECT m.name, m.mach_id, m.vendor, m.model, m.os, m.location, "
107              "m.contact, m.billing_contact, m.account_number, m.status, m.address,"
108              "m.owner_type, m.owner_id, m.acomment, m.ocomment, m.snet_id FROM machine m "
109              "WHERE %s", qual);
110      dosql(before);
111      beforec = 16;
112      name = xmalloc(0);
113      id = atoi(before[12]);
114      if (!strncmp(before[11], "USER", 4))
115        {
116          id_to_name(id, USERS_TABLE, &name);
117          strcpy(before[12], name);
118        }
119      else if (!strncmp(before[11], "LIST", 4))
120        {
121          id_to_name(id, LIST_TABLE, &name);
122          strcpy(before[12], name);
123        }
124      else if (!strncmp(before[11], "KERBEROS", 8))
125        {
126          id_to_name(id, STRINGS_TABLE, &name);
127          strcpy(before[12], name);
128        }
129      id = atoi(before[13]);
130      id_to_name(id, STRINGS_TABLE, &name);
131      strcpy(before[13], name);
132      id = atoi(before[14]);
133      id_to_name(id, STRINGS_TABLE, &name);
134      strcpy(before[14], name);
135      id = atoi(before[15]);
136      id_to_name(id, SUBNET_TABLE, &name);
137      strcpy(before[15], name);
138      break;
139    case HOSTALIAS_TABLE:
140      strcpy(before[0], argv[0]);
141      strcpy(before[1], argv[1]);
142      beforec = 2;
143      break;
144    case HWADDRMAP_TABLE:
145      strcpy(before[0], argv[0]);
146      strcpy(before[1], argv[1]);
147      beforec = 2;
148      break;
149    case CLUSTERS_TABLE:
150      sprintf(stmt_buf, "SELECT c.name, c.description, c.location, "
151              "c.clu_id FROM clusters c WHERE %s", qual);
152      dosql(before);
153      beforec = 4;
154      break;
155    case CONTAINERS_TABLE:
156      sprintf(stmt_buf, "SELECT c.name, c.description, c.location, c.contact, "
157              "c.acl_type, c.acl_id, c.cnt_id, c.list_id FROM containers c "
158              "WHERE %s", qual);
159      dosql(before);
160      beforec = 8;
161      name = xmalloc(0);
162      id = atoi(before[5]);
163      if (!strncmp(before[4], "USER", 4))
164        {
165          id_to_name(id, USERS_TABLE, &name);
166          strcpy(before[5], name);
167        }
168      else if (!strncmp(before[4], "LIST", 4))
169        {
170          id_to_name(id, LIST_TABLE, &name);
171          strcpy(before[5], name);
172        }
173      else if (!strncmp(before[4], "KERBEROS", 8))
174        {
175          id_to_name(id, STRINGS_TABLE, &name);
176          strcpy(before[5], name);
177        }
178      id = atoi(before[7]);
179      id_to_name(id, LIST_TABLE, &name);
180      strcpy(before[7], name);
181      break;
182    case MCMAP_TABLE:
183      strcpy(before[0], argv[0]);
184      strcpy(before[1], argv[1]);
185      beforec = 2;
186      break;
187    case MCNTMAP_TABLE:
188      strcpy(before[0], argv[0]);
189      strcpy(before[1], argv[1]);
190      name_to_id(before[0], MACHINE_TABLE, &id);
191      sprintf(before[2], "%d", id);
192      name_to_id(before[1], CONTAINERS_TABLE, &id);
193      sprintf(before[3], "%d", id);
194      name = xmalloc(0);
195      EXEC SQL SELECT list_id INTO :before[4] FROM containers
196        WHERE cnt_id = :id;
197      id = atoi(before[4]);
198      id_to_name(id, LIST_TABLE, &name);
199      strcpy(before[4], name);
200      beforec = 5;
201      break;
202    case SVC_TABLE:
203      strcpy(before[0], argv[0]);
204      strcpy(before[1], argv[1]);
205      strcpy(before[2], argv[2]);
206      beforec = 3;
207      break;
208    case FILESYS_TABLE:
209      sprintf(stmt_buf, "SELECT fs.label, fs.type, fs.mach_id, fs.name, "
210              "fs.mount, fs.rwaccess, fs.comments, fs.owner, fs.owners, "
211              "fs.createflg, fs.lockertype, fs.filsys_id FROM filesys fs "
212              "WHERE %s", qual);
213      dosql(before);
214      name = xmalloc(0);
215      id = atoi(before[2]);
216      id_to_name(id, MACHINE_TABLE, &name);
217      strcpy(before[2], name);
218      id = atoi(before[7]);
219      id_to_name(id, USERS_TABLE, &name);
220      strcpy(before[7], name);
221      id = atoi(before[8]);
222      id_to_name(id, LIST_TABLE, &name);
223      strcpy(before[8], name);
224      free(name);
225      beforec = 12;
226      break;
227    case QUOTA_TABLE:
228      strcpy(before[0], "?");
229      strcpy(before[1], argv[1]);
230      strcpy(before[2], "?");
231      sprintf(stmt_buf, "SELECT q.quota, fs.name FROM quota q, filesys fs "
232              "WHERE %s AND fs.filsys_id = q.filsys_id", qual);
233      dosql(&(before[3]));
234      strcpy(before[2], argv[1]);
235      beforec = 5;
236      break;
237    case LIST_TABLE:
238      sprintf(stmt_buf, "SELECT l.name, l.active, l.publicflg, l.hidden, "
239              "l.maillist, l.grouplist, l.gid, l.acl_type, l.acl_id, "
240              "l.description, l.list_id, l.nfsgroup FROM list l WHERE %s", qual);
241      dosql(before);
242      beforec = 12;
243      break;
244    case IMEMBERS_TABLE:
245      id = (int)(long)argv[0];
246      sprintf(stmt_buf, "SELECT active, publicflg, hidden, maillist, "
247              "grouplist, gid, nfsgroup FROM list WHERE list_id = %d", id);
248      dosql(&(before[3]));
249      name = xmalloc(0);
250      id_to_name(id, LIST_TABLE, &name);
251      name2 = xmalloc(0);
252      strcpy(before[0], name);
253      strcpy(before[1], argv[1]);
254      id = (int)(long)argv[2];
255      beforec = 11;
256      if (!strcmp(before[1], "USER"))
257        {
258          id_to_name(id, USERS_TABLE, &name2);
259          EXEC SQL SELECT status, users_id INTO :before[10], :before[12]
260            FROM users WHERE users_id = :id;
261          EXEC SQL SELECT list_id INTO :before[11] FROM list
262            WHERE name = :name;
263          beforec = 13;
264      }
265      else if (!strcmp(before[1], "LIST"))
266        {
267          id_to_name(id, LIST_TABLE, &name2);
268          EXEC SQL SELECT list_id INTO :before[10] FROM list
269            WHERE name = :name;
270          sprintf(before[11], "%d", id);
271          beforec = 12;
272        }
273      else if (!strcmp(before[1], "STRING") || !strcmp(before[1], "KERBEROS"))
274        {
275          id_to_name(id, STRINGS_TABLE, &name2);
276          EXEC SQL SELECT list_id INTO :before[10] FROM list
277            WHERE name = :name;
278        }
279      else if (!strcmp(before[1], "MACHINE"))
280        {
281          id_to_name(id, MACHINE_TABLE, &name2);
282          EXEC SQL SELECT list_id INTO :before[10] FROM list
283            WHERE name = :name;
284          sprintf(before[11], "%d", id);
285          beforec = 12;
286        }
287      strcpy(before[2], name2);
288      free(name);
289      free(name2);
290      break;
291    default:
292        /*
293        com_err(whoami, 0, "requested incremental on unexpected table `%s'",
294                table_name[table]);
295        */
296      break;
297    }
298}
299
300
301void incremental_clear_before(void)
302{
303  beforec = 0;
304}
305
306
307/* add an element to the incremental queue for the changed row */
308
309void incremental_after(enum tables table, char *qual, char **argv)
310{
311  char *name, *name2;
312  EXEC SQL BEGIN DECLARE SECTION;
313  int id;
314  EXEC SQL END DECLARE SECTION;
315  struct iupdate *iu;
316
317  switch (table)
318    {
319    case USERS_TABLE:
320      sprintf(stmt_buf, "SELECT u.login, u.unix_uid, u.shell, "
321              "u.winconsoleshell, u.last, u.first, u.middle, u.status, "
322              "u.clearid, u.type, u.users_id, u.winhomedir, u.winprofiledir, "
323              "u.potype FROM users u WHERE %s", qual);
324      dosql(after);
325      afterc = 14;
326      break;
327    case MACHINE_TABLE:
328      sprintf(stmt_buf, "SELECT m.name, m.mach_id, m.vendor, m.model, m.os, m.location, "
329              "m.contact, m.billing_contact, m.account_number, m.status, m.address,"
330              "m.owner_type, m.owner_id, m.acomment, m.ocomment, m.snet_id FROM machine m "
331              "WHERE %s", qual);
332      dosql(after);
333      afterc = 16;
334      name = xmalloc(0);
335      id = atoi(after[12]);
336      if (!strncmp(after[11], "USER", 4))
337        {
338          id_to_name(id, USERS_TABLE, &name);
339          strcpy(after[12], name);
340        }
341      else if (!strncmp(after[11], "LIST", 4))
342        {
343          id_to_name(id, LIST_TABLE, &name);
344          strcpy(after[12], name);
345        }
346      else if (!strncmp(after[11], "KERBEROS", 8))
347        {
348          id_to_name(id, STRINGS_TABLE, &name);
349          strcpy(after[12], name);
350        }
351      id = atoi(after[13]);
352      id_to_name(id, STRINGS_TABLE, &name);
353      strcpy(after[13], name);
354      id = atoi(after[14]);
355      id_to_name(id, STRINGS_TABLE, &name);
356      strcpy(after[14], name);
357      id = atoi(after[15]);
358      id_to_name(id, SUBNET_TABLE, &name);
359      strcpy(after[15], name);
360      break;
361    case HOSTALIAS_TABLE:
362      strcpy(after[0], argv[0]);
363      strcpy(after[1], argv[1]);
364      afterc = 2;
365      break;
366    case HWADDRMAP_TABLE:
367      strcpy(after[0], argv[0]);
368      strcpy(after[1], argv[1]);
369      afterc = 2;
370      break;
371    case CLUSTERS_TABLE:
372      sprintf(stmt_buf, "SELECT c.name, c.description, c.location, "
373              "c.clu_id FROM clusters c WHERE %s", qual);
374      dosql(after);
375      afterc = 4;
376      break;
377    case CONTAINERS_TABLE:
378      sprintf(stmt_buf, "SELECT c.name, c.description, c.location, c.contact, "
379              "c.acl_type, c.acl_id, c.cnt_id, c.list_id FROM containers c "
380              "WHERE %s", qual);
381      dosql(after);
382      afterc = 8;
383      name = xmalloc(0);
384      id = atoi(after[5]);
385      if (!strncmp(after[4], "USER", 4))
386        {
387          id_to_name(id, USERS_TABLE, &name);
388          strcpy(after[5], name);
389        }
390      else if (!strncmp(after[4], "LIST", 4))
391        {
392          id_to_name(id, LIST_TABLE, &name);
393          strcpy(after[5], name);
394        }
395      else if (!strncmp(after[4], "KERBEROS", 8))
396        {
397          id_to_name(id, STRINGS_TABLE, &name);
398          strcpy(after[5], name);
399        }
400      id = atoi(after[7]);
401      id_to_name(id, LIST_TABLE, &name);
402      strcpy(after[7], name);
403      break;
404    case MCMAP_TABLE:
405      strcpy(after[0], argv[0]);
406      strcpy(after[1], argv[1]);
407      afterc = 2;
408      break;
409    case MCNTMAP_TABLE:
410      strcpy(after[0], argv[0]);
411      strcpy(after[1], argv[1]);
412      name_to_id(after[0], MACHINE_TABLE, &id);
413      sprintf(after[2], "%d", id);
414      name_to_id(after[1], CONTAINERS_TABLE, &id);
415      sprintf(after[3], "%d", id);
416      name = xmalloc(0);
417      EXEC SQL SELECT list_id INTO :after[4] FROM containers
418        WHERE cnt_id = :id;
419      id = atoi(after[4]);
420      id_to_name(id, LIST_TABLE, &name);
421      strcpy(after[4], name);
422      afterc = 5;
423      break;
424    case SVC_TABLE:
425      strcpy(after[0], argv[0]);
426      strcpy(after[1], argv[1]);
427      strcpy(after[2], argv[2]);
428      afterc = 3;
429      break;
430    case FILESYS_TABLE:
431      sprintf(stmt_buf, "SELECT fs.label, fs.type, fs.mach_id, fs.name, "
432              "fs.mount, fs.rwaccess, fs.comments, fs.owner, fs.owners, "
433              "fs.createflg, fs.lockertype, fs.filsys_id FROM filesys fs "
434              "WHERE %s", qual);
435      dosql(after);
436      name = xmalloc(0);
437      id = atoi(after[2]);
438      id_to_name(id, MACHINE_TABLE, &name);
439      strcpy(after[2], name);
440      id = atoi(after[7]);
441      id_to_name(id, USERS_TABLE, &name);
442      strcpy(after[7], name);
443      id = atoi(after[8]);
444      id_to_name(id, LIST_TABLE, &name);
445      strcpy(after[8], name);
446      free(name);
447      afterc = 12;
448      break;
449    case QUOTA_TABLE:
450      strcpy(after[0], "?");
451      strcpy(after[1], argv[1]);
452      strcpy(after[2], "?");
453      sprintf(stmt_buf, "SELECT q.quota, fs.name FROM quota q, filesys fs "
454              "WHERE %s and fs.filsys_id = q.filsys_id and q.type = '%s'",
455              qual, argv[1]);
456      dosql(&(after[3]));
457      afterc = 5;
458      break;
459    case LIST_TABLE:
460      sprintf(stmt_buf, "SELECT l.name, l.active, l.publicflg, l.hidden, "
461              "l.maillist, l.grouplist, l.gid, l.acl_type, l.acl_id, "
462              "l.description, l.list_id, l.nfsgroup FROM list l WHERE %s", qual);
463      dosql(after);
464      afterc = 12;
465      break;
466    case IMEMBERS_TABLE:
467      id = (int)(long)argv[0];
468      sprintf(stmt_buf, "SELECT active, publicflg, hidden, maillist, "
469              "grouplist, gid, nfsgroup FROM list WHERE list_id = %d", id);
470      dosql(&(after[3]));
471      name = xmalloc(0);
472      id_to_name(id, LIST_TABLE, &name);
473      name2 = xmalloc(0);
474      strcpy(after[0], name);
475      strcpy(after[1], argv[1]);
476      id = (int)(long)argv[2];
477      afterc = 11;
478      if (!strcmp(after[1], "USER"))
479        {
480          id_to_name(id, USERS_TABLE, &name2);
481          EXEC SQL SELECT status, users_id INTO :after[10], :after[12]
482            FROM users WHERE users_id = :id;
483          EXEC SQL SELECT list_id INTO :after[11] FROM list
484            WHERE name = :name;
485          afterc = 13;
486        }
487      else if (!strcmp(after[1], "LIST"))
488        {
489          id_to_name(id, LIST_TABLE, &name2);
490          EXEC SQL SELECT list_id INTO :after[10] FROM list
491            WHERE name = :name;
492          sprintf(after[11], "%d", id);
493          afterc = 12;
494        }
495      else if (!strcmp(after[1], "STRING") || !strcmp(after[1], "KERBEROS"))
496        {
497          id_to_name(id, STRINGS_TABLE, &name2);
498          EXEC SQL SELECT list_id INTO :after[10] FROM list
499            WHERE name = :name;
500        }
501      else if (!strcmp(after[1], "MACHINE"))
502        {
503          id_to_name(id, MACHINE_TABLE, &name2);
504          EXEC SQL SELECT list_id INTO :after[10] FROM list
505            WHERE name = :name;
506          sprintf(after[11], "%d", id);
507          afterc = 12;
508        }
509      strcpy(after[2], name2);
510      free(name);
511      free(name2);
512      break;
513    case NO_TABLE:
514      afterc = 0;
515      table = beforetable;
516      break;
517    default:
518        /*
519        com_err(whoami, 0, "requested incremental on unexpected table `%s'",
520                table_name[table]);
521        */
522      break;
523    }
524
525  iu = xmalloc(sizeof(struct iupdate));
526  iu->table = table_name[table];
527  iu->beforec = beforec;
528  iu->before = copy_argv(before, beforec);
529  iu->afterc = afterc;
530  iu->after = copy_argv(after, afterc);
531  sq_save_data(incremental_sq, iu);
532}
533
534void incremental_clear_after(void)
535{
536  incremental_after(NO_TABLE, NULL, NULL);
537}
538
539
540/* Called when the current transaction is committed to start any queued
541 * incremental updates.  This caches the update table the first time it
542 * is called.
543 */
544
545struct inc_cache {
546  struct inc_cache *next;
547  char *table, *service;
548};
549
550
551void incremental_update(void)
552{
553  static int inited = 0;
554  static struct inc_cache *cache;
555  struct inc_cache *c;
556  EXEC SQL BEGIN DECLARE SECTION;
557  char tab[INCREMENTAL_TABLE_NAME_SIZE], serv[INCREMENTAL_SERVICE_SIZE];
558  EXEC SQL END DECLARE SECTION;
559  struct iupdate *iu, *iu_save;
560
561  if (!inited)
562    {
563      inited++;
564
565      EXEC SQL DECLARE inc CURSOR FOR SELECT table_name, service
566        FROM incremental;
567      EXEC SQL OPEN inc;
568      while (1)
569        {
570          EXEC SQL FETCH inc INTO :tab, :serv;
571          if (sqlca.sqlcode)
572            break;
573          c = xmalloc(sizeof(struct inc_cache));
574          c->next = cache;
575          c->table = xstrdup(strtrim(tab));
576          c->service = xstrdup(strtrim(serv));
577          cache = c;
578        }
579      EXEC SQL CLOSE inc;
580      EXEC SQL COMMIT WORK;
581    }
582
583  while (sq_remove_data(incremental_sq, &iu))
584    {
585      for (c = cache; c; c = c->next)
586        {
587          if (!strcmp(c->table, iu->table))
588            {
589              iu->service = c->service;
590              iu_save = xmalloc(sizeof(struct iupdate));
591              iu_save->service = iu->service;
592              iu_save->table = iu->table;
593              iu_save->beforec = iu->beforec;
594              iu_save->afterc = iu->afterc;
595              iu_save->before = copy_argv(iu->before, iu->beforec);
596              iu_save->after = copy_argv(iu->after, iu->afterc);
597              sq_save_data(incremental_exec, iu_save);
598            }
599        }
600      if (!c)
601        {
602          free_argv(iu->before, iu->beforec);
603          free_argv(iu->after, iu->afterc);
604          free(iu);
605        }
606    }
607  if (inc_running == 0)
608    next_incremental();
609}
610
611/* Pro*C 2.2.4 can't cope with the sigset_t below, at least in Solaris 2.6.
612   We add DEFINE=_PROC_ to the proc invocation and then #ifndef that around
613   this function so proc will pass it through without reading it. */
614
615#ifndef _PROC_
616void next_incremental(void)
617{
618  struct iupdate *iu;
619  char *argv[MAXARGC * 2 + 4], cafter[3], cbefore[3], prog[MAXPATHLEN];
620  int i;
621  sigset_t sigs;
622
623  if (!incremental_exec)
624    incremental_init();
625
626  if (sq_empty(incremental_exec) ||
627      (inc_running && now - inc_started < INC_TIMEOUT))
628    return;
629
630  if (inc_running)
631    critical_alert(whoami, "moirad", "incremental timeout on pid %d", inc_pid);
632
633  sq_remove_data(incremental_exec, &iu);
634  argv[1] = iu->table;
635  sprintf(cbefore, "%d", iu->beforec);
636  argv[2] = cbefore;
637  sprintf(cafter, "%d", iu->afterc);
638  argv[3] = cafter;
639  for (i = 0; i < iu->beforec; i++)
640    argv[4 + i] = iu->before[i];
641  for (i = 0; i < iu->afterc; i++)
642    argv[4 + iu->beforec + i] = iu->after[i];
643
644  sprintf(prog, "%s/%s.incr", BIN_DIR, iu->service);
645  argv[0] = prog;
646  argv[4 + iu->beforec + iu->afterc] = 0;
647
648  sigemptyset(&sigs);
649  sigaddset(&sigs, SIGCHLD);
650  sigprocmask(SIG_BLOCK, &sigs, NULL);
651  inc_pid = vfork();
652  switch (inc_pid)
653    {
654    case 0:
655      execv(prog, argv);
656      _exit(1);
657    case -1:
658      critical_alert(whoami, "moirad", "Failed to start incremental update %s", prog);
659      break;
660    default:
661      inc_running = 1;
662      inc_started = now;
663    }
664  sigprocmask(SIG_UNBLOCK, &sigs, NULL);
665
666  free_argv(iu->before, iu->beforec);
667  free_argv(iu->after, iu->afterc);
668  free(iu);
669}
670#endif
671
672/* Called when the current transaction is aborted to throw away any queued
673 * incremental updates
674 */
675
676void incremental_flush(void)
677{
678  struct iupdate *iu;
679
680  while (sq_get_data(incremental_sq, &iu))
681    {
682      free_argv(iu->before, iu->beforec);
683      free_argv(iu->after, iu->afterc);
684      free(iu);
685    }
686  sq_destroy(incremental_sq);
687  incremental_sq = sq_create();
688}
689
690
691char **copy_argv(char **argv, int argc)
692{
693  char **ret = xmalloc(sizeof(char *) * argc);
694  while (--argc >= 0)
695    ret[argc] = xstrdup(strtrim(argv[argc]));
696  return ret;
697}
698
699void free_argv(char **argv, int argc)
700{
701  while (--argc >= 0)
702    free(argv[argc]);
703  free(argv);
704}
705
706int table_num(char *name)
707{
708  int i;
709
710  for (i = num_tables - 1; i; i--)
711    {
712      if (!strcmp(table_name[i], name))
713        break;
714    }
715
716  return i; /* 0 = "none" if no match */
717}
Note: See TracBrowser for help on using the repository browser.