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