Annotation of mandoc/mdoc.c, Revision 1.3
1.3 ! kristaps 1: /* $Id: mdoc.c,v 1.2 2008/12/15 02:23:12 kristaps Exp $ */
1.1 kristaps 2: /*
3: * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the
7: * above copyright notice and this permission notice appear in all
8: * copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11: * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12: * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13: * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14: * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15: * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16: * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17: * PERFORMANCE OF THIS SOFTWARE.
18: */
19: #include <assert.h>
20: #include <ctype.h>
21: #include <err.h>
22: #include <stdarg.h>
23: #include <stdlib.h>
24: #include <stdio.h>
25: #include <string.h>
26:
27: #include "private.h"
28:
29: const char *const __mdoc_macronames[MDOC_MAX] = {
30: "\\\"", "Dd", "Dt", "Os",
31: "Sh", "Ss", "Pp", "D1",
32: "Dl", "Bd", "Ed", "Bl",
33: "El", "It", "Ad", "An",
34: "Ar", "Cd", "Cm", "Dv",
35: "Er", "Ev", "Ex", "Fa",
36: "Fd", "Fl", "Fn", "Ft",
37: "Ic", "In", "Li", "Nd",
38: "Nm", "Op", "Ot", "Pa",
39: "Rv", "St", "Va", "Vt",
40: /* LINTED */
41: "Xr", "\%A", "\%B", "\%D",
42: /* LINTED */
43: "\%I", "\%J", "\%N", "\%O",
44: /* LINTED */
45: "\%P", "\%R", "\%T", "\%V",
46: "Ac", "Ao", "Aq", "At",
47: "Bc", "Bf", "Bo", "Bq",
48: "Bsx", "Bx", "Db", "Dc",
49: "Do", "Dq", "Ec", "Ef",
50: "Em", "Eo", "Fx", "Ms",
51: "No", "Ns", "Nx", "Ox",
52: "Pc", "Pf", "Po", "Pq",
53: "Qc", "Ql", "Qo", "Qq",
54: "Re", "Rs", "Sc", "So",
55: "Sq", "Sm", "Sx", "Sy",
56: "Tn", "Ux", "Xc", "Xo",
57: "Fo", "Fc", "Oo", "Oc",
58: "Bk", "Ek", "Bt", "Hf",
59: "Fr", "Ud",
60: };
61:
62: const char *const __mdoc_argnames[MDOC_ARG_MAX] = {
63: "split", "nosplit", "ragged",
64: "unfilled", "literal", "file",
65: "offset", "bullet", "dash",
66: "hyphen", "item", "enum",
67: "tag", "diag", "hang",
68: "ohang", "inset", "column",
69: "width", "compact", "std",
70: "p1003.1-88", "p1003.1-90", "p1003.1-96",
71: "p1003.1-2001", "p1003.1-2004", "p1003.1",
72: "p1003.1b", "p1003.1b-93", "p1003.1c-95",
73: "p1003.1g-2000", "p1003.2-92", "p1387.2-95",
74: "p1003.2", "p1387.2", "isoC-90",
75: "isoC-amd1", "isoC-tcor1", "isoC-tcor2",
76: "isoC-99", "ansiC", "ansiC-89",
77: "ansiC-99", "ieee754", "iso8802-3",
78: "xpg3", "xpg4", "xpg4.2",
79: "xpg4.3", "xbd5", "xcu5",
80: "xsh5", "xns5", "xns5.2d2.0",
81: "xcurses4.2", "susv2", "susv3",
82: "svid4", "filled", "words",
83: };
84:
85: const struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
86: { NULL, 0 }, /* \" */
1.3 ! kristaps 87: { macro_prologue_ddate, 0 }, /* Dd */
! 88: { macro_prologue_dtitle, 0 }, /* Dt */
1.1 kristaps 89: { NULL, 0 }, /* Os */
90: { macro_scoped_implicit, 0 }, /* Sh */
91: { macro_scoped_implicit, 0 }, /* Ss */
92: { NULL, 0 }, /* Pp */
93: { NULL, 0 }, /* D1 */
94: { NULL, 0 }, /* Dl */
95: { NULL, 0 }, /* Bd */
96: { NULL, 0 }, /* Ed */
97: { NULL, 0 }, /* Bl */
98: { NULL, 0 }, /* El */
99: { NULL, 0 }, /* It */
100: { macro_text, MDOC_CALLABLE }, /* Ad */
101: { NULL, 0 }, /* An */
102: { macro_text, MDOC_CALLABLE }, /* Ar */
103: { NULL, 0 }, /* Cd */
104: { macro_text, MDOC_CALLABLE }, /* Cm */
105: { macro_text, MDOC_CALLABLE }, /* Dv */
106: { macro_text, MDOC_CALLABLE }, /* Er */
107: { macro_text, MDOC_CALLABLE }, /* Ev */
108: { NULL, 0 }, /* Ex */
109: { macro_text, MDOC_CALLABLE }, /* Fa */
110: { NULL, 0 }, /* Fd */
111: { macro_text, MDOC_CALLABLE }, /* Fl */
112: { NULL, 0 }, /* Fn */
113: { macro_text, 0 }, /* Ft */
114: { macro_text, MDOC_CALLABLE }, /* Ic */
115: { NULL, 0 }, /* In */
116: { macro_text, MDOC_CALLABLE }, /* Li */
117: { NULL, 0 }, /* Nd */
118: { NULL, 0 }, /* Nm */
119: { NULL, 0 }, /* Op */
120: { NULL, 0 }, /* Ot */
121: { macro_text, MDOC_CALLABLE }, /* Pa */
122: { NULL, 0 }, /* Rv */
123: { NULL, 0 }, /* St */
124: { macro_text, MDOC_CALLABLE }, /* Va */
125: { macro_text, MDOC_CALLABLE }, /* Vt */
126: { NULL, 0 }, /* Xr */
127: { NULL, 0 }, /* %A */
128: { NULL, 0 }, /* %B */
129: { NULL, 0 }, /* %D */
130: { NULL, 0 }, /* %I */
131: { NULL, 0 }, /* %J */
132: { NULL, 0 }, /* %N */
133: { NULL, 0 }, /* %O */
134: { NULL, 0 }, /* %P */
135: { NULL, 0 }, /* %R */
136: { NULL, 0 }, /* %T */
137: { NULL, 0 }, /* %V */
138: { NULL, 0 }, /* Ac */
139: { NULL, 0 }, /* Ao */
140: { NULL, 0 }, /* Aq */
141: { NULL, 0 }, /* At */
142: { NULL, 0 }, /* Bc */
143: { NULL, 0 }, /* Bf */
144: { NULL, 0 }, /* Bo */
145: { NULL, 0 }, /* Bq */
146: { NULL, 0 }, /* Bsx */
147: { NULL, 0 }, /* Bx */
148: { NULL, 0 }, /* Db */
149: { NULL, 0 }, /* Dc */
150: { NULL, 0 }, /* Do */
151: { NULL, 0 }, /* Dq */
152: { NULL, 0 }, /* Ec */
153: { NULL, 0 }, /* Ef */
154: { macro_text, MDOC_CALLABLE }, /* Em */
155: { NULL, 0 }, /* Eo */
156: { NULL, 0 }, /* Fx */
157: { macro_text, 0 }, /* Ms */
158: { NULL, 0 }, /* No */
159: { NULL, 0 }, /* Ns */
160: { NULL, 0 }, /* Nx */
161: { NULL, 0 }, /* Ox */
162: { NULL, 0 }, /* Pc */
163: { NULL, 0 }, /* Pf */
164: { NULL, 0 }, /* Po */
165: { NULL, 0 }, /* Pq */
166: { NULL, 0 }, /* Qc */
167: { NULL, 0 }, /* Ql */
168: { NULL, 0 }, /* Qo */
169: { NULL, 0 }, /* Qq */
170: { NULL, 0 }, /* Re */
171: { NULL, 0 }, /* Rs */
172: { NULL, 0 }, /* Sc */
173: { NULL, 0 }, /* So */
174: { NULL, 0 }, /* Sq */
175: { NULL, 0 }, /* Sm */
176: { NULL, 0 }, /* Sx */
177: { NULL, 0 }, /* Sy */
178: { macro_text, MDOC_CALLABLE }, /* Tn */
179: { NULL, 0 }, /* Ux */
180: { NULL, 0 }, /* Xc */
181: { NULL, 0 }, /* Xo */
182: { NULL, 0 }, /* Fo */
183: { NULL, 0 }, /* Fc */
184: { NULL, 0 }, /* Oo */
185: { NULL, 0 }, /* Oc */
186: { NULL, 0 }, /* Bk */
187: { NULL, 0 }, /* Ek */
188: { NULL, 0 }, /* Bt */
189: { NULL, 0 }, /* Hf */
190: { NULL, 0 }, /* Fr */
191: { NULL, 0 }, /* Ud */
192: };
193:
194: const char * const *mdoc_macronames = __mdoc_macronames;
195: const char * const *mdoc_argnames = __mdoc_argnames;
196: const struct mdoc_macro * const mdoc_macros = __mdoc_macros;
197:
198:
199: static void *xcalloc(size_t, size_t);
200: static char *xstrdup(const char *);
201:
202: static struct mdoc_arg *argdup(size_t, const struct mdoc_arg *);
203: static void argfree(size_t, struct mdoc_arg *);
204: static void argcpy(struct mdoc_arg *,
205: const struct mdoc_arg *);
206: static char **paramdup(size_t, const char **);
207: static void paramfree(size_t, char **);
208:
209: static void mdoc_node_freelist(struct mdoc_node *);
210: static void mdoc_node_append(struct mdoc *, int,
211: struct mdoc_node *);
212: static void mdoc_elem_free(struct mdoc_elem *);
213: static void mdoc_text_free(struct mdoc_text *);
214:
215:
216: const struct mdoc_node *
217: mdoc_result(struct mdoc *mdoc)
218: {
219:
220: return(mdoc->first);
221: }
222:
223:
224: void
225: mdoc_free(struct mdoc *mdoc)
226: {
227:
228: if (mdoc->first)
229: mdoc_node_freelist(mdoc->first);
230: if (mdoc->htab)
231: mdoc_hash_free(mdoc->htab);
232:
233: free(mdoc);
234: }
235:
236:
237: struct mdoc *
238: mdoc_alloc(void *data, const struct mdoc_cb *cb)
239: {
240: struct mdoc *p;
241:
242: p = xcalloc(1, sizeof(struct mdoc));
243:
244: p->data = data;
245: (void)memcpy(&p->cb, cb, sizeof(struct mdoc_cb));
246:
247: p->htab = mdoc_hash_alloc();
248: return(p);
249: }
250:
251:
252: static void *
253: xcalloc(size_t num, size_t sz)
254: {
255: void *p;
256:
257: if (NULL == (p = calloc(num, sz)))
258: err(EXIT_FAILURE, "calloc");
259: return(p);
260: }
261:
262:
263: static char *
264: xstrdup(const char *p)
265: {
266: char *pp;
267:
268: if (NULL == (pp = strdup(p)))
269: err(EXIT_FAILURE, "strdup");
270: return(pp);
271: }
272:
273:
274: int
275: mdoc_parseln(struct mdoc *mdoc, char *buf)
276: {
277: int c, i;
278: char tmp[5];
279:
280: if ('.' != *buf) {
281: /* TODO. */
282: return(1);
283: }
284:
285: if (buf[1] && '\\' == buf[1])
286: if (buf[2] && '\"' == buf[2])
287: return(1);
288:
289: i = 1;
290: while (buf[i] && ! isspace(buf[i]) && i < (int)sizeof(tmp))
291: i++;
292:
293: if (i == (int)sizeof(tmp))
294: return(mdoc_err(mdoc, -1, 1, ERR_MACRO_NOTSUP));
295: else if (i <= 2)
296: return(mdoc_err(mdoc, -1, 1, ERR_MACRO_NOTSUP));
297:
298: i--;
299:
1.2 kristaps 300: (void)memcpy(tmp, buf + 1, (size_t)i);
1.1 kristaps 301: tmp[i++] = 0;
302:
303: if (MDOC_MAX == (c = mdoc_find(mdoc, tmp)))
304: return(mdoc_err(mdoc, c, 1, ERR_MACRO_NOTSUP));
305:
306: while (buf[i] && isspace(buf[i]))
307: i++;
308:
1.3 ! kristaps 309: return(mdoc_macro(mdoc, c, 1, &i, buf));
1.1 kristaps 310: }
311:
312:
313: void
314: mdoc_msg(struct mdoc *mdoc, int pos, const char *fmt, ...)
315: {
316: va_list ap;
317: char buf[256];
318:
319: if (NULL == mdoc->cb.mdoc_msg)
320: return;
321:
322: va_start(ap, fmt);
323: (void)vsnprintf(buf, sizeof(buf), fmt, ap);
324: va_end(ap);
325:
326: (*mdoc->cb.mdoc_msg)(mdoc->data, pos, buf);
327: }
328:
329:
330: int
331: mdoc_err(struct mdoc *mdoc, int tok, int pos, enum mdoc_err type)
332: {
333:
334: if (NULL == mdoc->cb.mdoc_err)
335: return(0);
336: return((*mdoc->cb.mdoc_err)(mdoc->data, tok, pos, type));
337: }
338:
339:
340: int
341: mdoc_warn(struct mdoc *mdoc, int tok, int pos, enum mdoc_warn type)
342: {
343:
344: if (NULL == mdoc->cb.mdoc_warn)
345: return(0);
346: return((*mdoc->cb.mdoc_warn)(mdoc->data, tok, pos, type));
347: }
348:
349:
350: int
351: mdoc_macro(struct mdoc *mdoc, int tok, int ppos, int *pos, char *buf)
352: {
353:
354: if (NULL == (mdoc_macros[tok].fp)) {
355: (void)mdoc_err(mdoc, tok, ppos, ERR_MACRO_NOTSUP);
356: return(0);
1.3 ! kristaps 357: }
! 358:
! 359: if (1 != ppos && ! (MDOC_CALLABLE & mdoc_macros[tok].flags)) {
1.1 kristaps 360: (void)mdoc_err(mdoc, tok, ppos, ERR_MACRO_NOTCALL);
361: return(0);
362: }
363:
364: return((*mdoc_macros[tok].fp)(mdoc, tok, ppos, pos, buf));
365: }
366:
367:
368: static void
369: mdoc_node_append(struct mdoc *mdoc, int pos, struct mdoc_node *p)
370: {
371: const char *nn, *on, *nt, *ot, *act;
372:
373: switch (p->type) {
374: case (MDOC_TEXT):
375: nn = "<text>";
376: nt = "text";
377: break;
378: case (MDOC_BODY):
379: nn = mdoc_macronames[p->data.body.tok];
380: nt = "body";
381: break;
382: case (MDOC_ELEM):
383: nn = mdoc_macronames[p->data.elem.tok];
384: nt = "elem";
385: break;
386: case (MDOC_HEAD):
387: nn = mdoc_macronames[p->data.head.tok];
388: nt = "head";
389: break;
390: case (MDOC_BLOCK):
391: nn = mdoc_macronames[p->data.block.tok];
392: nt = "block";
393: break;
1.2 kristaps 394: default:
395: abort();
396: /* NOTREACHED */
1.1 kristaps 397: }
398:
399: if (NULL == mdoc->first) {
400: assert(NULL == mdoc->last);
401: mdoc->first = p;
402: mdoc->last = p;
403: mdoc_msg(mdoc, pos, "parse: root %s `%s'", nt, nn);
404: return;
405: }
406:
407: switch (mdoc->last->type) {
408: case (MDOC_TEXT):
409: on = "<text>";
410: ot = "text";
411: break;
412: case (MDOC_BODY):
413: on = mdoc_macronames[mdoc->last->data.body.tok];
414: ot = "body";
415: break;
416: case (MDOC_ELEM):
417: on = mdoc_macronames[mdoc->last->data.elem.tok];
418: ot = "elem";
419: break;
420: case (MDOC_HEAD):
421: on = mdoc_macronames[mdoc->last->data.head.tok];
422: ot = "head";
423: break;
424: case (MDOC_BLOCK):
425: on = mdoc_macronames[mdoc->last->data.block.tok];
426: ot = "block";
427: break;
1.2 kristaps 428: default:
429: abort();
430: /* NOTREACHED */
1.1 kristaps 431: }
432:
433: switch (p->type) {
434: case (MDOC_BODY):
435: switch (mdoc->last->type) {
436: case (MDOC_BLOCK):
437: p->parent = mdoc->last;
438: mdoc->last->child = p;
439: act = "child";
440: break;
441: case (MDOC_HEAD):
442: p->parent = mdoc->last->parent;
443: mdoc->last->next = p;
444: act = "sibling";
445: break;
446: default:
447: abort();
448: /* NOTREACHED */
449: }
450: break;
451: case (MDOC_HEAD):
452: assert(mdoc->last->type == MDOC_BLOCK);
453: p->parent = mdoc->last;
454: mdoc->last->child = p;
455: act = "child";
456: break;
457: default:
458: switch (mdoc->last->type) {
459: case (MDOC_BODY):
460: /* FALLTHROUGH */
461: case (MDOC_HEAD):
462: p->parent = mdoc->last->parent;
463: mdoc->last->child = p;
464: act = "child";
465: break;
466: default:
467: p->parent = mdoc->last->parent;
468: mdoc->last->next = p;
469: act = "sibling";
470: break;
471: }
472: break;
473: }
474:
475: mdoc_msg(mdoc, pos, "parse: %s `%s' %s %s `%s'",
476: nt, nn, act, ot, on);
477: mdoc->last = p;
478: }
479:
480:
481: void
482: mdoc_head_alloc(struct mdoc *mdoc, int pos, int tok,
483: size_t paramsz, const char **params)
484: {
485: struct mdoc_node *p;
486:
487: assert(mdoc->first);
488: assert(mdoc->last);
489: assert(mdoc->last->type == MDOC_BLOCK);
490: assert(mdoc->last->data.block.tok == tok);
491:
492: p = xcalloc(1, sizeof(struct mdoc_node));
493: p->type = MDOC_HEAD;
494: p->data.head.tok = tok;
495: p->data.head.sz = paramsz;
496: p->data.head.args = paramdup(paramsz, params);
497:
498: mdoc_node_append(mdoc, pos, p);
499: }
500:
501:
502: void
503: mdoc_body_alloc(struct mdoc *mdoc, int pos, int tok)
504: {
505: struct mdoc_node *p;
506:
507: assert(mdoc->first);
508: assert(mdoc->last);
509: assert((mdoc->last->type == MDOC_BLOCK) ||
510: (mdoc->last->type == MDOC_HEAD));
511: if (mdoc->last->type == MDOC_BLOCK)
512: assert(mdoc->last->data.block.tok == tok);
513: else
514: assert(mdoc->last->data.head.tok == tok);
515:
516: p = xcalloc(1, sizeof(struct mdoc_node));
517:
518: p->type = MDOC_BODY;
519: p->data.body.tok = tok;
520:
521: mdoc_node_append(mdoc, pos, p);
522: }
523:
524:
525: void
526: mdoc_block_alloc(struct mdoc *mdoc, int pos, int tok,
527: size_t argsz, const struct mdoc_arg *args)
528: {
529: struct mdoc_node *p;
530:
531: p = xcalloc(1, sizeof(struct mdoc_node));
532:
533: p->type = MDOC_BLOCK;
534: p->data.block.tok = tok;
535: p->data.block.argc = argsz;
536: p->data.block.argv = argdup(argsz, args);
537:
538: mdoc_node_append(mdoc, pos, p);
539: }
540:
541:
542: void
543: mdoc_elem_alloc(struct mdoc *mdoc, int pos, int tok,
544: size_t argsz, const struct mdoc_arg *args,
545: size_t paramsz, const char **params)
546: {
547: struct mdoc_node *p;
548:
549: p = xcalloc(1, sizeof(struct mdoc_node));
550: p->type = MDOC_ELEM;
551: p->data.elem.tok = tok;
552: p->data.elem.sz = paramsz;
553: p->data.elem.args = paramdup(paramsz, params);
554: p->data.elem.argc = argsz;
555: p->data.elem.argv = argdup(argsz, args);
556:
557: mdoc_node_append(mdoc, pos, p);
558: }
559:
560:
561: void
562: mdoc_word_alloc(struct mdoc *mdoc, int pos, const char *word)
563: {
564: struct mdoc_node *p;
565:
566: p = xcalloc(1, sizeof(struct mdoc_node));
567: p->type = MDOC_TEXT;
568: p->data.text.string = xstrdup(word);
569:
570: mdoc_node_append(mdoc, pos, p);
571: }
572:
573:
574: static void
575: argfree(size_t sz, struct mdoc_arg *p)
576: {
1.2 kristaps 577: int i, j;
1.1 kristaps 578:
579: if (0 == sz)
580: return;
581:
582: assert(p);
1.2 kristaps 583: /* LINTED */
584: for (i = 0; i < (int)sz; i++)
1.1 kristaps 585: if (p[i].sz > 0) {
586: assert(p[i].value);
1.2 kristaps 587: /* LINTED */
588: for (j = 0; j < (int)p[i].sz; j++)
1.1 kristaps 589: free(p[i].value[j]);
590: }
591: free(p);
592: }
593:
594:
595: static void
596: mdoc_elem_free(struct mdoc_elem *p)
597: {
598:
599: paramfree(p->sz, p->args);
600: argfree(p->argc, p->argv);
601: }
602:
603:
604: static void
605: mdoc_block_free(struct mdoc_block *p)
606: {
607:
608: argfree(p->argc, p->argv);
609: }
610:
611:
612: static void
613: mdoc_text_free(struct mdoc_text *p)
614: {
615:
616: if (p->string)
617: free(p->string);
618: }
619:
620:
621: static void
622: mdoc_head_free(struct mdoc_head *p)
623: {
624:
625: paramfree(p->sz, p->args);
626: }
627:
628:
629: void
630: mdoc_node_free(struct mdoc_node *p)
631: {
632:
633: switch (p->type) {
634: case (MDOC_TEXT):
635: mdoc_text_free(&p->data.text);
636: break;
637: case (MDOC_ELEM):
638: mdoc_elem_free(&p->data.elem);
639: break;
640: case (MDOC_BLOCK):
641: mdoc_block_free(&p->data.block);
642: break;
643: case (MDOC_HEAD):
644: mdoc_head_free(&p->data.head);
645: break;
646: default:
647: break;
648: }
649:
650: free(p);
651: }
652:
653:
654: static void
655: mdoc_node_freelist(struct mdoc_node *p)
656: {
657:
658: if (p->child)
659: mdoc_node_freelist(p->child);
660: if (p->next)
661: mdoc_node_freelist(p->next);
662:
663: mdoc_node_free(p);
664: }
665:
666:
667: int
668: mdoc_find(const struct mdoc *mdoc, const char *key)
669: {
670:
671: return(mdoc_hash_find(mdoc->htab, key));
672: }
673:
674:
675: static void
676: argcpy(struct mdoc_arg *dst, const struct mdoc_arg *src)
677: {
1.2 kristaps 678: int i;
1.1 kristaps 679:
680: dst->arg = src->arg;
681: if (0 == (dst->sz = src->sz))
682: return;
683: dst->value = xcalloc(dst->sz, sizeof(char *));
1.2 kristaps 684: for (i = 0; i < (int)dst->sz; i++)
1.1 kristaps 685: dst->value[i] = xstrdup(src->value[i]);
686: }
687:
688:
689: static struct mdoc_arg *
690: argdup(size_t argsz, const struct mdoc_arg *args)
691: {
692: struct mdoc_arg *pp;
1.2 kristaps 693: int i;
1.1 kristaps 694:
695: if (0 == argsz)
696: return(NULL);
697:
698: pp = xcalloc((size_t)argsz, sizeof(struct mdoc_arg));
1.2 kristaps 699: for (i = 0; i < (int)argsz; i++)
1.1 kristaps 700: argcpy(&pp[i], &args[i]);
701:
702: return(pp);
703: }
704:
705:
706: static void
707: paramfree(size_t sz, char **p)
708: {
1.2 kristaps 709: int i;
1.1 kristaps 710:
711: if (0 == sz)
712: return;
713:
714: assert(p);
1.2 kristaps 715: /* LINTED */
716: for (i = 0; i < (int)sz; i++)
1.1 kristaps 717: free(p[i]);
718: free(p);
719: }
720:
721:
722: static char **
723: paramdup(size_t sz, const char **p)
724: {
725: char **pp;
1.2 kristaps 726: int i;
1.1 kristaps 727:
728: if (0 == sz)
729: return(NULL);
730:
731: pp = xcalloc(sz, sizeof(char *));
1.2 kristaps 732: for (i = 0; i < (int)sz; i++)
1.1 kristaps 733: pp[i] = xstrdup(p[i]);
734:
735: return(pp);
736: }
CVSweb