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