1 /* $Id: reorg.c,v 1.6 2019/05/01 11:03:31 schwarze Exp $ */
2 /*
3 * Copyright (c) 2019 Ingo Schwarze <schwarze@openbsd.org>
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 above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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.
16 */
17 #include "string.h"
18
19 #include "node.h"
20 #include "reorg.h"
21
22 /*
23 * The implementation of the tree reorganizer.
24 */
25
26 static void
reorg_root(struct pnode * root,const char * sec)27 reorg_root(struct pnode *root, const char *sec)
28 {
29 struct pnode *date, *info, *name, *vol, *nc;
30
31 if (root == NULL)
32 return;
33
34 /* Collect prologue information. */
35
36 if ((date = pnode_takefirst(root, NODE_PUBDATE)) == NULL &&
37 (date = pnode_takefirst(root, NODE_DATE)) == NULL) {
38 date = pnode_alloc(NULL);
39 pnode_alloc_text(date, "$Mdocdate" "$");
40 }
41 date->node = NODE_DATE;
42 date->parent = root;
43
44 name = vol = NULL;
45 if ((nc = pnode_findfirst(root, NODE_REFMETA)) != NULL) {
46 name = pnode_takefirst(nc, NODE_REFENTRYTITLE);
47 vol = pnode_takefirst(nc, NODE_MANVOLNUM);
48 }
49 if (name == NULL) {
50 name = pnode_alloc(NULL);
51 name->node = NODE_REFENTRYTITLE;
52 name->parent = root;
53 pnode_alloc_text(name,
54 pnode_getattr_raw(root, ATTRKEY_ID, "UNKNOWN"));
55 }
56 if (vol == NULL || sec != NULL) {
57 pnode_unlink(vol);
58 vol = pnode_alloc(NULL);
59 vol->node = NODE_MANVOLNUM;
60 vol->parent = root;
61 pnode_alloc_text(vol, sec == NULL ? "1" : sec);
62 }
63
64 /* Insert prologue information at the beginning. */
65
66 if (pnode_findfirst(root, NODE_REFNAMEDIV) == NULL &&
67 ((info = pnode_findfirst(root, NODE_BOOKINFO)) != NULL ||
68 (info = pnode_findfirst(root, NODE_REFENTRYINFO)) != NULL)) {
69 if ((nc = pnode_takefirst(info, NODE_ABSTRACT)) != NULL)
70 TAILQ_INSERT_HEAD(&root->childq, nc, child);
71 if ((nc = pnode_takefirst(info, NODE_TITLE)) != NULL)
72 TAILQ_INSERT_HEAD(&root->childq, nc, child);
73 }
74 TAILQ_INSERT_HEAD(&root->childq, vol, child);
75 TAILQ_INSERT_HEAD(&root->childq, name, child);
76 TAILQ_INSERT_HEAD(&root->childq, date, child);
77 }
78
79 static void
reorg_refentry(struct pnode * n)80 reorg_refentry(struct pnode *n)
81 {
82 struct pnode *info, *meta, *nc, *title;
83 struct pnode *match, *later;
84
85 /* Collect nodes that remained behind from the prologue. */
86
87 meta = NULL;
88 info = pnode_takefirst(n, NODE_BOOKINFO);
89 if (info != NULL && TAILQ_FIRST(&info->childq) == NULL) {
90 pnode_unlink(info);
91 info = NULL;
92 }
93 if (info == NULL) {
94 info = pnode_takefirst(n, NODE_REFENTRYINFO);
95 if (info != NULL && TAILQ_FIRST(&info->childq) == NULL) {
96 pnode_unlink(info);
97 info = NULL;
98 }
99 if (info == NULL)
100 info = pnode_takefirst(n, NODE_INFO);
101 meta = pnode_takefirst(n, NODE_REFMETA);
102 if (meta != NULL && TAILQ_FIRST(&meta->childq) == NULL) {
103 pnode_unlink(meta);
104 meta = NULL;
105 }
106 }
107 if (info == NULL && meta == NULL)
108 return;
109
110 /*
111 * Find the best place to put this information.
112 * Use the last existing AUTHORS node, if any.
113 * Otherwise, put it behind all standard sections that
114 * conventionally precede AUTHORS, and also behind any
115 * non-standard sections that follow the last of these,
116 * but before the next standard section.
117 */
118
119 match = later = NULL;
120 TAILQ_FOREACH(nc, &n->childq, child) {
121 switch (nc->node) {
122 case NODE_REFENTRY:
123 case NODE_REFNAMEDIV:
124 case NODE_REFSYNOPSISDIV:
125 later = NULL;
126 continue;
127 case NODE_APPENDIX:
128 case NODE_INDEX:
129 if (later == NULL)
130 later = nc;
131 continue;
132 default:
133 break;
134 }
135 if ((title = pnode_findfirst(nc, NODE_TITLE)) == NULL ||
136 (title = TAILQ_FIRST(&title->childq)) == NULL ||
137 title->node != NODE_TEXT)
138 continue;
139 if (strcasecmp(title->b, "AUTHORS") == 0 ||
140 strcasecmp(title->b, "AUTHOR") == 0)
141 match = nc;
142 else if (strcasecmp(title->b, "NAME") == 0 ||
143 strcasecmp(title->b, "SYNOPSIS") == 0 ||
144 strcasecmp(title->b, "DESCRIPTION") == 0 ||
145 strcasecmp(title->b, "RETURN VALUES") == 0 ||
146 strcasecmp(title->b, "ENVIRONMENT") == 0 ||
147 strcasecmp(title->b, "FILES") == 0 ||
148 strcasecmp(title->b, "EXIT STATUS") == 0 ||
149 strcasecmp(title->b, "EXAMPLES") == 0 ||
150 strcasecmp(title->b, "DIAGNOSTICS") == 0 ||
151 strcasecmp(title->b, "ERRORS") == 0 ||
152 strcasecmp(title->b, "SEE ALSO") == 0 ||
153 strcasecmp(title->b, "STANDARDS") == 0 ||
154 strcasecmp(title->b, "HISTORY") == 0)
155 later = NULL;
156 else if ((strcasecmp(title->b, "CAVEATS") == 0 ||
157 strcasecmp(title->b, "BUGS") == 0) &&
158 later == NULL)
159 later = nc;
160 }
161
162 /*
163 * If no AUTHORS section was found, create one from scratch,
164 * and insert that at the place selected earlier.
165 */
166
167 if (match == NULL) {
168 match = pnode_alloc(NULL);
169 match->node = NODE_SECTION;
170 match->flags |= NFLAG_SPC;
171 match->parent = n;
172 nc = pnode_alloc(match);
173 nc->node = NODE_TITLE;
174 nc->flags |= NFLAG_SPC;
175 nc = pnode_alloc_text(nc, "AUTHORS");
176 nc->flags |= NFLAG_SPC;
177 if (later == NULL)
178 TAILQ_INSERT_TAIL(&n->childq, match, child);
179 else
180 TAILQ_INSERT_BEFORE(later, match, child);
181 }
182
183 /*
184 * Dump the stuff excised at the beginning
185 * into this AUTHORS section.
186 */
187
188 if (info != NULL)
189 TAILQ_INSERT_TAIL(&match->childq, info, child);
190 if (meta != NULL)
191 TAILQ_INSERT_TAIL(&match->childq, meta, child);
192 }
193
194 static void
default_title(struct pnode * n,const char * title)195 default_title(struct pnode *n, const char *title)
196 {
197 struct pnode *nc;
198
199 if (n->parent == NULL)
200 return;
201
202 TAILQ_FOREACH(nc, &n->childq, child)
203 if (nc->node == NODE_TITLE)
204 return;
205
206 nc = pnode_alloc(NULL);
207 nc->node = NODE_TITLE;
208 nc->parent = n;
209 TAILQ_INSERT_HEAD(&n->childq, nc, child);
210 pnode_alloc_text(nc, title);
211 }
212
213 static void
reorg_function(struct pnode * n)214 reorg_function(struct pnode *n)
215 {
216 struct pnode *nc;
217 size_t sz;
218
219 if ((nc = TAILQ_FIRST(&n->childq)) != NULL &&
220 nc->node == NODE_TEXT &&
221 TAILQ_NEXT(nc, child) == NULL &&
222 (sz = strlen(nc->b)) > 2 &&
223 nc->b[sz - 2] == '(' && nc->b[sz - 1] == ')')
224 nc->b[sz - 2] = '\0';
225 }
226
227 static void
reorg_recurse(struct pnode * n)228 reorg_recurse(struct pnode *n)
229 {
230 struct pnode *nc;
231
232 if (n == NULL)
233 return;
234
235 switch (n->node) {
236 case NODE_ABSTRACT:
237 default_title(n, "Abstract");
238 n->node = NODE_SECTION;
239 break;
240 case NODE_APPENDIX:
241 if (n->parent == NULL)
242 reorg_refentry(n);
243 default_title(n, "Appendix");
244 break;
245 case NODE_CAUTION:
246 default_title(n, "Caution");
247 n->node = NODE_NOTE;
248 break;
249 case NODE_FUNCTION:
250 reorg_function(n);
251 break;
252 case NODE_LEGALNOTICE:
253 default_title(n, "Legal Notice");
254 n->node = NODE_SIMPLESECT;
255 break;
256 case NODE_NOTE:
257 default_title(n, "Note");
258 break;
259 case NODE_PREFACE:
260 if (n->parent == NULL)
261 reorg_refentry(n);
262 default_title(n, "Preface");
263 n->node = NODE_SECTION;
264 break;
265 case NODE_REFENTRY:
266 reorg_refentry(n);
267 break;
268 case NODE_SECTION:
269 if (n->parent == NULL)
270 reorg_refentry(n);
271 /* FALLTHROUGH */
272 case NODE_SIMPLESECT:
273 default_title(n, "Untitled");
274 break;
275 case NODE_TIP:
276 default_title(n, "Tip");
277 n->node = NODE_NOTE;
278 break;
279 case NODE_WARNING:
280 default_title(n, "Warning");
281 n->node = NODE_NOTE;
282 break;
283 default:
284 break;
285 }
286
287 TAILQ_FOREACH(nc, &n->childq, child)
288 reorg_recurse(nc);
289 }
290
291 void
ptree_reorg(struct ptree * tree,const char * sec)292 ptree_reorg(struct ptree *tree, const char *sec)
293 {
294 reorg_root(tree->root, sec);
295 reorg_recurse(tree->root);
296 }
297