Annotation of src/usr.bin/mandoc/man.c, Revision 1.58
1.58 ! schwarze 1: /* $Id: man.c,v 1.57 2011/03/20 23:36:42 schwarze Exp $ */
1.1 kristaps 2: /*
1.52 schwarze 3: * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.1 kristaps 4: *
5: * Permission to use, copy, modify, and distribute this software for any
1.2 schwarze 6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
1.1 kristaps 8: *
1.2 schwarze 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 kristaps 16: */
1.14 schwarze 17: #include <sys/types.h>
18:
1.1 kristaps 19: #include <assert.h>
20: #include <stdarg.h>
21: #include <stdlib.h>
22: #include <stdio.h>
23: #include <string.h>
24:
1.33 schwarze 25: #include "mandoc.h"
1.1 kristaps 26: #include "libman.h"
1.16 schwarze 27: #include "libmandoc.h"
1.1 kristaps 28:
29: const char *const __man_macronames[MAN_MAX] = {
1.3 schwarze 30: "br", "TH", "SH", "SS",
1.1 kristaps 31: "TP", "LP", "PP", "P",
32: "IP", "HP", "SM", "SB",
33: "BI", "IB", "BR", "RB",
34: "R", "B", "I", "IR",
1.50 schwarze 35: "RI", "na", "sp", "nf",
36: "fi", "RE", "RS", "DT",
37: "UC", "PD", "AT", "in",
1.52 schwarze 38: "ft"
1.1 kristaps 39: };
40:
41: const char * const *man_macronames = __man_macronames;
42:
1.54 schwarze 43: static struct man_node *man_node_alloc(struct man *, int, int,
1.22 schwarze 44: enum man_type, enum mant);
1.1 kristaps 45: static int man_node_append(struct man *,
46: struct man_node *);
1.22 schwarze 47: static void man_node_free(struct man_node *);
48: static void man_node_unlink(struct man *,
49: struct man_node *);
1.31 schwarze 50: static int man_ptext(struct man *, int, char *, int);
51: static int man_pmacro(struct man *, int, char *, int);
1.1 kristaps 52: static void man_free1(struct man *);
1.16 schwarze 53: static void man_alloc1(struct man *);
1.52 schwarze 54: static int man_descope(struct man *, int, int);
1.1 kristaps 55:
56:
57: const struct man_node *
58: man_node(const struct man *m)
59: {
60:
1.53 schwarze 61: assert( ! (MAN_HALT & m->flags));
62: return(m->first);
1.1 kristaps 63: }
64:
65:
66: const struct man_meta *
67: man_meta(const struct man *m)
68: {
69:
1.53 schwarze 70: assert( ! (MAN_HALT & m->flags));
71: return(&m->meta);
1.1 kristaps 72: }
73:
74:
1.16 schwarze 75: void
1.1 kristaps 76: man_reset(struct man *man)
77: {
78:
79: man_free1(man);
1.16 schwarze 80: man_alloc1(man);
1.1 kristaps 81: }
82:
83:
84: void
85: man_free(struct man *man)
86: {
87:
88: man_free1(man);
89: free(man);
90: }
91:
92:
93: struct man *
1.40 schwarze 94: man_alloc(struct regset *regs, void *data, mandocmsg msg)
1.1 kristaps 95: {
96: struct man *p;
97:
1.16 schwarze 98: p = mandoc_calloc(1, sizeof(struct man));
1.1 kristaps 99:
1.13 schwarze 100: man_hash_init();
1.1 kristaps 101: p->data = data;
1.33 schwarze 102: p->msg = msg;
1.35 schwarze 103: p->regs = regs;
1.16 schwarze 104:
105: man_alloc1(p);
1.1 kristaps 106: return(p);
107: }
108:
109:
110: int
111: man_endparse(struct man *m)
112: {
113:
1.53 schwarze 114: assert( ! (MAN_HALT & m->flags));
115: if (man_macroend(m))
1.1 kristaps 116: return(1);
117: m->flags |= MAN_HALT;
118: return(0);
119: }
120:
121:
122: int
1.31 schwarze 123: man_parseln(struct man *m, int ln, char *buf, int offs)
1.1 kristaps 124: {
1.25 schwarze 125:
1.54 schwarze 126: m->flags |= MAN_NEWLINE;
127:
1.53 schwarze 128: assert( ! (MAN_HALT & m->flags));
1.31 schwarze 129: return(('.' == buf[offs] || '\'' == buf[offs]) ?
130: man_pmacro(m, ln, buf, offs) :
131: man_ptext(m, ln, buf, offs));
1.1 kristaps 132: }
133:
134:
135: static void
136: man_free1(struct man *man)
137: {
138:
139: if (man->first)
1.22 schwarze 140: man_node_delete(man, man->first);
1.1 kristaps 141: if (man->meta.title)
142: free(man->meta.title);
143: if (man->meta.source)
144: free(man->meta.source);
1.56 schwarze 145: if (man->meta.date)
146: free(man->meta.date);
1.1 kristaps 147: if (man->meta.vol)
148: free(man->meta.vol);
1.29 schwarze 149: if (man->meta.msec)
150: free(man->meta.msec);
1.1 kristaps 151: }
152:
153:
1.16 schwarze 154: static void
1.1 kristaps 155: man_alloc1(struct man *m)
156: {
157:
1.16 schwarze 158: memset(&m->meta, 0, sizeof(struct man_meta));
1.1 kristaps 159: m->flags = 0;
1.16 schwarze 160: m->last = mandoc_calloc(1, sizeof(struct man_node));
1.1 kristaps 161: m->first = m->last;
162: m->last->type = MAN_ROOT;
1.22 schwarze 163: m->last->tok = MAN_MAX;
1.1 kristaps 164: m->next = MAN_NEXT_CHILD;
165: }
166:
167:
168: static int
169: man_node_append(struct man *man, struct man_node *p)
170: {
171:
172: assert(man->last);
173: assert(man->first);
174: assert(MAN_ROOT != p->type);
175:
176: switch (man->next) {
177: case (MAN_NEXT_SIBLING):
178: man->last->next = p;
179: p->prev = man->last;
180: p->parent = man->last->parent;
181: break;
182: case (MAN_NEXT_CHILD):
183: man->last->child = p;
184: p->parent = man->last;
185: break;
186: default:
187: abort();
188: /* NOTREACHED */
189: }
1.5 schwarze 190:
1.22 schwarze 191: assert(p->parent);
1.5 schwarze 192: p->parent->nchild++;
1.1 kristaps 193:
1.9 schwarze 194: if ( ! man_valid_pre(man, p))
195: return(0);
196:
197: switch (p->type) {
198: case (MAN_HEAD):
199: assert(MAN_BLOCK == p->parent->type);
200: p->parent->head = p;
201: break;
202: case (MAN_BODY):
203: assert(MAN_BLOCK == p->parent->type);
204: p->parent->body = p;
205: break;
206: default:
207: break;
208: }
209:
1.1 kristaps 210: man->last = p;
211:
212: switch (p->type) {
1.52 schwarze 213: case (MAN_TBL):
214: /* FALLTHROUGH */
1.1 kristaps 215: case (MAN_TEXT):
216: if ( ! man_valid_post(man))
217: return(0);
218: break;
219: default:
220: break;
221: }
222:
223: return(1);
224: }
225:
226:
227: static struct man_node *
1.54 schwarze 228: man_node_alloc(struct man *m, int line, int pos,
229: enum man_type type, enum mant tok)
1.1 kristaps 230: {
231: struct man_node *p;
232:
1.16 schwarze 233: p = mandoc_calloc(1, sizeof(struct man_node));
1.1 kristaps 234: p->line = line;
235: p->pos = pos;
236: p->type = type;
237: p->tok = tok;
1.54 schwarze 238:
239: if (MAN_NEWLINE & m->flags)
240: p->flags |= MAN_LINE;
241: m->flags &= ~MAN_NEWLINE;
1.1 kristaps 242: return(p);
243: }
244:
245:
246: int
1.22 schwarze 247: man_elem_alloc(struct man *m, int line, int pos, enum mant tok)
1.1 kristaps 248: {
249: struct man_node *p;
250:
1.54 schwarze 251: p = man_node_alloc(m, line, pos, MAN_ELEM, tok);
1.10 schwarze 252: if ( ! man_node_append(m, p))
253: return(0);
254: m->next = MAN_NEXT_CHILD;
255: return(1);
1.1 kristaps 256: }
257:
258:
259: int
1.22 schwarze 260: man_head_alloc(struct man *m, int line, int pos, enum mant tok)
1.9 schwarze 261: {
262: struct man_node *p;
263:
1.54 schwarze 264: p = man_node_alloc(m, line, pos, MAN_HEAD, tok);
1.9 schwarze 265: if ( ! man_node_append(m, p))
266: return(0);
267: m->next = MAN_NEXT_CHILD;
268: return(1);
269: }
270:
271:
272: int
1.22 schwarze 273: man_body_alloc(struct man *m, int line, int pos, enum mant tok)
1.9 schwarze 274: {
275: struct man_node *p;
276:
1.54 schwarze 277: p = man_node_alloc(m, line, pos, MAN_BODY, tok);
1.9 schwarze 278: if ( ! man_node_append(m, p))
279: return(0);
280: m->next = MAN_NEXT_CHILD;
281: return(1);
282: }
283:
284:
285: int
1.22 schwarze 286: man_block_alloc(struct man *m, int line, int pos, enum mant tok)
1.9 schwarze 287: {
288: struct man_node *p;
289:
1.54 schwarze 290: p = man_node_alloc(m, line, pos, MAN_BLOCK, tok);
1.9 schwarze 291: if ( ! man_node_append(m, p))
292: return(0);
293: m->next = MAN_NEXT_CHILD;
294: return(1);
295: }
296:
1.27 schwarze 297: int
298: man_word_alloc(struct man *m, int line, int pos, const char *word)
1.1 kristaps 299: {
1.10 schwarze 300: struct man_node *n;
1.27 schwarze 301: size_t sv, len;
302:
303: len = strlen(word);
1.1 kristaps 304:
1.54 schwarze 305: n = man_node_alloc(m, line, pos, MAN_TEXT, MAN_MAX);
1.16 schwarze 306: n->string = mandoc_malloc(len + 1);
1.27 schwarze 307: sv = strlcpy(n->string, word, len + 1);
1.10 schwarze 308:
309: /* Prohibit truncation. */
310: assert(sv < len + 1);
311:
312: if ( ! man_node_append(m, n))
1.1 kristaps 313: return(0);
1.27 schwarze 314:
1.10 schwarze 315: m->next = MAN_NEXT_SIBLING;
316: return(1);
317: }
318:
319:
1.22 schwarze 320: /*
321: * Free all of the resources held by a node. This does NOT unlink a
322: * node from its context; for that, see man_node_unlink().
323: */
324: static void
1.1 kristaps 325: man_node_free(struct man_node *p)
326: {
327:
328: if (p->string)
329: free(p->string);
330: free(p);
331: }
332:
333:
334: void
1.22 schwarze 335: man_node_delete(struct man *m, struct man_node *p)
1.1 kristaps 336: {
337:
1.22 schwarze 338: while (p->child)
339: man_node_delete(m, p->child);
340:
341: man_node_unlink(m, p);
1.1 kristaps 342: man_node_free(p);
343: }
344:
1.57 schwarze 345: int
346: man_addeqn(struct man *m, const struct eqn *ep)
347: {
348: struct man_node *n;
349:
350: assert( ! (MAN_HALT & m->flags));
351:
352: n = man_node_alloc(m, ep->line, ep->pos, MAN_EQN, MAN_MAX);
353: n->eqn = ep;
354:
355: if ( ! man_node_append(m, n))
356: return(0);
357:
358: m->next = MAN_NEXT_SIBLING;
359: return(man_descope(m, ep->line, ep->pos));
360: }
1.1 kristaps 361:
1.52 schwarze 362: int
363: man_addspan(struct man *m, const struct tbl_span *sp)
364: {
1.55 schwarze 365: struct man_node *n;
1.52 schwarze 366:
1.53 schwarze 367: assert( ! (MAN_HALT & m->flags));
1.55 schwarze 368:
369: n = man_node_alloc(m, sp->line, 0, MAN_TBL, MAN_MAX);
370: n->span = sp;
371:
372: if ( ! man_node_append(m, n))
1.52 schwarze 373: return(0);
1.55 schwarze 374:
375: m->next = MAN_NEXT_SIBLING;
376: return(man_descope(m, sp->line, 0));
1.52 schwarze 377: }
378:
379: static int
380: man_descope(struct man *m, int line, int offs)
381: {
382: /*
383: * Co-ordinate what happens with having a next-line scope open:
384: * first close out the element scope (if applicable), then close
385: * out the block scope (also if applicable).
386: */
387:
388: if (MAN_ELINE & m->flags) {
389: m->flags &= ~MAN_ELINE;
390: if ( ! man_unscope(m, m->last->parent, MANDOCERR_MAX))
391: return(0);
392: }
393:
394: if ( ! (MAN_BLINE & m->flags))
395: return(1);
396: m->flags &= ~MAN_BLINE;
397:
398: if ( ! man_unscope(m, m->last->parent, MANDOCERR_MAX))
399: return(0);
400: return(man_body_alloc(m, line, offs, m->last->tok));
401: }
402:
403:
1.1 kristaps 404: static int
1.31 schwarze 405: man_ptext(struct man *m, int line, char *buf, int offs)
1.1 kristaps 406: {
1.27 schwarze 407: int i;
1.26 schwarze 408:
409: /* Ignore bogus comments. */
410:
1.31 schwarze 411: if ('\\' == buf[offs] &&
412: '.' == buf[offs + 1] &&
1.51 schwarze 413: '"' == buf[offs + 2]) {
414: man_pmsg(m, line, offs, MANDOCERR_BADCOMMENT);
415: return(1);
416: }
1.10 schwarze 417:
418: /* Literal free-form text whitespace is preserved. */
419:
420: if (MAN_LITERAL & m->flags) {
1.31 schwarze 421: if ( ! man_word_alloc(m, line, offs, buf + offs))
1.10 schwarze 422: return(0);
1.52 schwarze 423: return(man_descope(m, line, offs));
1.10 schwarze 424: }
425:
1.27 schwarze 426: /* Pump blank lines directly into the backend. */
1.10 schwarze 427:
1.31 schwarze 428: for (i = offs; ' ' == buf[i]; i++)
1.10 schwarze 429: /* Skip leading whitespace. */ ;
1.18 schwarze 430:
431: if ('\0' == buf[i]) {
1.27 schwarze 432: /* Allocate a blank entry. */
1.31 schwarze 433: if ( ! man_word_alloc(m, line, offs, ""))
1.10 schwarze 434: return(0);
1.52 schwarze 435: return(man_descope(m, line, offs));
1.10 schwarze 436: }
1.1 kristaps 437:
1.27 schwarze 438: /*
439: * Warn if the last un-escaped character is whitespace. Then
440: * strip away the remaining spaces (tabs stay!).
441: */
1.18 schwarze 442:
1.27 schwarze 443: i = (int)strlen(buf);
444: assert(i);
1.18 schwarze 445:
1.27 schwarze 446: if (' ' == buf[i - 1] || '\t' == buf[i - 1]) {
447: if (i > 1 && '\\' != buf[i - 2])
1.51 schwarze 448: man_pmsg(m, line, i - 1, MANDOCERR_EOLNSPACE);
1.18 schwarze 449:
1.27 schwarze 450: for (--i; i && ' ' == buf[i]; i--)
451: /* Spin back to non-space. */ ;
1.10 schwarze 452:
1.27 schwarze 453: /* Jump ahead of escaped whitespace. */
454: i += '\\' == buf[i] ? 2 : 1;
1.18 schwarze 455:
1.27 schwarze 456: buf[i] = '\0';
1.10 schwarze 457: }
1.9 schwarze 458:
1.31 schwarze 459: if ( ! man_word_alloc(m, line, offs, buf + offs))
1.1 kristaps 460: return(0);
1.28 schwarze 461:
462: /*
463: * End-of-sentence check. If the last character is an unescaped
464: * EOS character, then flag the node as being the end of a
465: * sentence. The front-end will know how to interpret this.
466: */
467:
468: assert(i);
1.37 schwarze 469: if (mandoc_eos(buf, (size_t)i, 0))
1.28 schwarze 470: m->last->flags |= MAN_EOS;
1.10 schwarze 471:
1.52 schwarze 472: return(man_descope(m, line, offs));
1.1 kristaps 473: }
474:
475:
1.53 schwarze 476: static int
1.31 schwarze 477: man_pmacro(struct man *m, int ln, char *buf, int offs)
1.1 kristaps 478: {
1.24 schwarze 479: int i, j, ppos;
1.22 schwarze 480: enum mant tok;
1.10 schwarze 481: char mac[5];
482: struct man_node *n;
1.1 kristaps 483:
484: /* Comments and empties are quickly ignored. */
485:
1.31 schwarze 486: offs++;
487:
488: if ('\0' == buf[offs])
1.17 schwarze 489: return(1);
1.1 kristaps 490:
1.31 schwarze 491: i = offs;
1.1 kristaps 492:
1.23 schwarze 493: /*
494: * Skip whitespace between the control character and initial
495: * text. "Whitespace" is both spaces and tabs.
496: */
1.27 schwarze 497:
1.23 schwarze 498: if (' ' == buf[i] || '\t' == buf[i]) {
1.1 kristaps 499: i++;
1.23 schwarze 500: while (buf[i] && (' ' == buf[i] || '\t' == buf[i]))
1.1 kristaps 501: i++;
1.18 schwarze 502: if ('\0' == buf[i])
1.1 kristaps 503: goto out;
504: }
505:
506: ppos = i;
507:
1.39 schwarze 508: /*
509: * Copy the first word into a nil-terminated buffer.
510: * Stop copying when a tab, space, or eoln is encountered.
511: */
1.1 kristaps 512:
1.39 schwarze 513: j = 0;
514: while (j < 4 && '\0' != buf[i] && ' ' != buf[i] && '\t' != buf[i])
515: mac[j++] = buf[i++];
1.17 schwarze 516: mac[j] = '\0';
1.1 kristaps 517:
1.40 schwarze 518: tok = (j > 0 && j < 4) ? man_hash_find(mac) : MAN_MAX;
519: if (MAN_MAX == tok) {
1.44 schwarze 520: man_vmsg(m, MANDOCERR_MACRO, ln, ppos, "%s", buf + ppos - 1);
1.1 kristaps 521: return(1);
522: }
523:
524: /* The macro is sane. Jump to the next word. */
525:
526: while (buf[i] && ' ' == buf[i])
527: i++;
1.18 schwarze 528:
1.27 schwarze 529: /*
530: * Trailing whitespace. Note that tabs are allowed to be passed
531: * into the parser as "text", so we only warn about spaces here.
532: */
1.18 schwarze 533:
534: if ('\0' == buf[i] && ' ' == buf[i - 1])
1.51 schwarze 535: man_pmsg(m, ln, i - 1, MANDOCERR_EOLNSPACE);
1.1 kristaps 536:
1.21 schwarze 537: /*
1.46 schwarze 538: * Remove prior ELINE macro, as it's being clobbered by a new
1.21 schwarze 539: * macro. Note that NSCOPED macros do not close out ELINE
540: * macros---they don't print text---so we let those slip by.
541: */
542:
1.22 schwarze 543: if ( ! (MAN_NSCOPED & man_macros[tok].flags) &&
1.21 schwarze 544: m->flags & MAN_ELINE) {
1.10 schwarze 545: n = m->last;
1.46 schwarze 546: assert(MAN_TEXT != n->type);
1.21 schwarze 547:
1.49 schwarze 548: /* Remove repeated NSCOPED macros causing ELINE. */
549:
1.46 schwarze 550: if (MAN_NSCOPED & man_macros[n->tok].flags)
551: n = n->parent;
1.21 schwarze 552:
1.46 schwarze 553: man_vmsg(m, MANDOCERR_LINESCOPE, n->line, n->pos,
1.49 schwarze 554: "%s", man_macronames[n->tok]);
1.10 schwarze 555:
1.22 schwarze 556: man_node_delete(m, n);
1.10 schwarze 557: m->flags &= ~MAN_ELINE;
558: }
559:
1.24 schwarze 560: /*
561: * Save the fact that we're in the next-line for a block. In
562: * this way, embedded roff instructions can "remember" state
563: * when they exit.
564: */
565:
566: if (MAN_BLINE & m->flags)
567: m->flags |= MAN_BPLINE;
568:
569: /* Call to handler... */
1.1 kristaps 570:
1.22 schwarze 571: assert(man_macros[tok].fp);
572: if ( ! (*man_macros[tok].fp)(m, tok, ln, ppos, &i, buf))
1.1 kristaps 573: goto err;
574:
575: out:
1.21 schwarze 576: /*
577: * We weren't in a block-line scope when entering the
578: * above-parsed macro, so return.
579: */
580:
1.24 schwarze 581: if ( ! (MAN_BPLINE & m->flags)) {
1.21 schwarze 582: m->flags &= ~MAN_ILINE;
1.9 schwarze 583: return(1);
1.21 schwarze 584: }
1.24 schwarze 585: m->flags &= ~MAN_BPLINE;
1.21 schwarze 586:
587: /*
588: * If we're in a block scope, then allow this macro to slip by
589: * without closing scope around it.
590: */
591:
592: if (MAN_ILINE & m->flags) {
593: m->flags &= ~MAN_ILINE;
594: return(1);
595: }
1.9 schwarze 596:
597: /*
598: * If we've opened a new next-line element scope, then return
599: * now, as the next line will close out the block scope.
600: */
601:
602: if (MAN_ELINE & m->flags)
603: return(1);
604:
605: /* Close out the block scope opened in the prior line. */
1.1 kristaps 606:
1.9 schwarze 607: assert(MAN_BLINE & m->flags);
608: m->flags &= ~MAN_BLINE;
1.1 kristaps 609:
1.33 schwarze 610: if ( ! man_unscope(m, m->last->parent, MANDOCERR_MAX))
1.9 schwarze 611: return(0);
1.31 schwarze 612: return(man_body_alloc(m, ln, offs, m->last->tok));
1.1 kristaps 613:
614: err: /* Error out. */
615:
616: m->flags |= MAN_HALT;
617: return(0);
618: }
619:
620:
1.58 ! schwarze 621: void
1.33 schwarze 622: man_vmsg(struct man *man, enum mandocerr t,
623: int ln, int pos, const char *fmt, ...)
1.1 kristaps 624: {
625: char buf[256];
626: va_list ap;
627:
628: va_start(ap, fmt);
1.33 schwarze 629: vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
1.1 kristaps 630: va_end(ap);
1.58 ! schwarze 631: (*man->msg)(t, man->data, ln, pos, buf);
1.21 schwarze 632: }
633:
634:
1.22 schwarze 635: /*
636: * Unlink a node from its context. If "m" is provided, the last parse
637: * point will also be adjusted accordingly.
638: */
639: static void
1.21 schwarze 640: man_node_unlink(struct man *m, struct man_node *n)
641: {
642:
1.22 schwarze 643: /* Adjust siblings. */
644:
645: if (n->prev)
1.21 schwarze 646: n->prev->next = n->next;
1.22 schwarze 647: if (n->next)
648: n->next->prev = n->prev;
649:
650: /* Adjust parent. */
651:
652: if (n->parent) {
653: n->parent->nchild--;
654: if (n->parent->child == n)
655: n->parent->child = n->prev ? n->prev : n->next;
656: }
657:
658: /* Adjust parse point, if applicable. */
659:
660: if (m && m->last == n) {
661: /*XXX: this can occur when bailing from validation. */
662: /*assert(NULL == n->next);*/
663: if (n->prev) {
1.21 schwarze 664: m->last = n->prev;
665: m->next = MAN_NEXT_SIBLING;
1.22 schwarze 666: } else {
1.21 schwarze 667: m->last = n->parent;
668: m->next = MAN_NEXT_CHILD;
669: }
670: }
671:
1.22 schwarze 672: if (m && m->first == n)
673: m->first = NULL;
1.4 schwarze 674: }