]> git.cameronkatri.com Git - mandoc.git/blob - mdoc.c
Linted.
[mandoc.git] / mdoc.c
1 /* $Id: mdoc.c,v 1.2 2008/12/15 02:23:12 kristaps Exp $ */
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
304 (void)memcpy(tmp, buf + 1, (size_t)i);
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;
401 default:
402 abort();
403 /* NOTREACHED */
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;
435 default:
436 abort();
437 /* NOTREACHED */
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 {
584 int i, j;
585
586 if (0 == sz)
587 return;
588
589 assert(p);
590 /* LINTED */
591 for (i = 0; i < (int)sz; i++)
592 if (p[i].sz > 0) {
593 assert(p[i].value);
594 /* LINTED */
595 for (j = 0; j < (int)p[i].sz; j++)
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 {
685 int i;
686
687 dst->arg = src->arg;
688 if (0 == (dst->sz = src->sz))
689 return;
690 dst->value = xcalloc(dst->sz, sizeof(char *));
691 for (i = 0; i < (int)dst->sz; i++)
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;
700 int i;
701
702 if (0 == argsz)
703 return(NULL);
704
705 pp = xcalloc((size_t)argsz, sizeof(struct mdoc_arg));
706 for (i = 0; i < (int)argsz; i++)
707 argcpy(&pp[i], &args[i]);
708
709 return(pp);
710 }
711
712
713 static void
714 paramfree(size_t sz, char **p)
715 {
716 int i;
717
718 if (0 == sz)
719 return;
720
721 assert(p);
722 /* LINTED */
723 for (i = 0; i < (int)sz; i++)
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;
733 int i;
734
735 if (0 == sz)
736 return(NULL);
737
738 pp = xcalloc(sz, sizeof(char *));
739 for (i = 0; i < (int)sz; i++)
740 pp[i] = xstrdup(p[i]);
741
742 return(pp);
743 }