1/*	$OpenBSD: dbm_dump.c,v 1.3 2024/05/14 00:31:48 schwarze Exp $ */
2/*
3 * Copyright (c) 2016 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 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.
16 *
17 * Function to dump an on-disk read-only mandoc database
18 * in diff(1)able format for debugging purposes.
19 */
20#include <err.h>
21#include <regex.h>
22#include <stdint.h>
23#include <stdio.h>
24#include <string.h>
25
26#include "mansearch.h"
27#include "dbm_map.h"
28#include "dbm.h"
29
30union ptr {
31	const char	*c;
32	const int32_t	*i;
33};
34
35static void		 dump(void);
36static const char	*dump_macro(union ptr, int32_t);
37static const char	*dump_macros(union ptr);
38static const char	*dump_pages(union ptr);
39static void		 dump_str(const char **);
40static void		 dump_lst(const char **);
41static void		 pchk(const char *, const char **, const char *, int);
42
43
44int
45main(int argc, char *argv[])
46{
47	if (argc != 2)
48		errx(1, "usage: dump filename");
49	if (dbm_open(argv[1]) == -1)
50		err(1, "%s", argv[1]);
51	dump();
52	dbm_close();
53	return 0;
54}
55
56static void
57dump(void)
58{
59	union ptr	 p, macros, end;
60
61	p.i = dbm_getint(0);
62	printf("initial magic 0x%08x\n", be32toh(*p.i++));
63	printf("version       0x%08x\n", be32toh(*p.i++));
64	printf("macros offset 0x%08x\n", be32toh(*p.i));
65	macros.i = dbm_get(*p.i++);
66	printf("end offset    0x%08x\n", be32toh(*p.i));
67	end.i = dbm_get(*p.i++);
68	p.c = dump_pages(p);
69	pchk(macros.c, &p.c, "macros", 3);
70	p.c = dump_macros(p);
71	pchk(end.c, &p.c, "end", 0);
72	printf("final magic   0x%08x\n", be32toh(*p.i));
73}
74
75static const char *
76dump_pages(union ptr p)
77{
78	const char	*name0, *sect0, *arch0, *desc0, *file0;
79	const char	*namep, *sectp, *archp, *descp, *filep;
80	int32_t		 i, npages;
81
82	npages = be32toh(*p.i++);
83	printf("page count    %d\n", npages);
84	if (npages == 0)
85		return p.c;
86	namep = name0 = dbm_get(p.i[0]);
87	sectp = sect0 = dbm_get(p.i[1]);
88	archp = arch0 = p.i[2] == 0 ? NULL : dbm_get(p.i[2]);
89	descp = desc0 = dbm_get(p.i[3]);
90	filep = file0 = dbm_get(p.i[4]);
91	printf("=== PAGES ===\n");
92	for (i = 0; i < npages; i++) {
93		pchk(dbm_get(*p.i++), &namep, "name", 0);
94		printf("page name ");
95		dump_lst(&namep);
96		pchk(dbm_get(*p.i++), &sectp, "sect", 0);
97		printf("page sect ");
98		dump_lst(&sectp);
99		if (*p.i++) {
100			if (arch0 == NULL)
101				archp = arch0 = dbm_get(p.i[-1]);
102			else
103				pchk(dbm_get(p.i[-1]), &archp, "arch", 0);
104			printf("page arch ");
105			dump_lst(&archp);
106		}
107		pchk(dbm_get(*p.i++), &descp, "desc", 0);
108		printf("page desc # ");
109		dump_str(&descp);
110		printf("\npage file ");
111		pchk(dbm_get(*p.i++), &filep, "file", 0);
112		if (filep == NULL) {
113			printf("# (NULL)\n");
114			continue;
115		}
116		switch(*filep++) {
117		case 1:
118			printf("src ");
119			break;
120		case 2:
121			printf("cat ");
122			break;
123		default:
124			printf("UNKNOWN FORMAT %d ", filep[-1]);
125			break;
126		}
127		dump_lst(&filep);
128	}
129	printf("=== END OF PAGES ===\n");
130	pchk(name0, &p.c, "name0", 0);
131	pchk(sect0, &namep, "sect0", 0);
132	if (arch0 != NULL) {
133		pchk(arch0, &sectp, "arch0", 0);
134		pchk(desc0, &archp, "desc0", 0);
135	} else
136		pchk(desc0, &sectp, "desc0", 0);
137	pchk(file0, &descp, "file0", 0);
138	return filep;
139}
140
141static const char *
142dump_macros(union ptr p)
143{
144	union ptr	 macro0, macrop;
145	int32_t		 i, nmacros;
146
147	nmacros = be32toh(*p.i++);
148	printf("macros count  %d\n", nmacros);
149	if (nmacros == 0)
150		return p.c;
151	macrop.i = macro0.i = dbm_get(*p.i);
152	printf("=== MACROS ===\n");
153	for (i = 0; i < nmacros; i++) {
154		pchk(dbm_get(*p.i++), &macrop.c, "macro", 0);
155		macrop.c = dump_macro(macrop, i);
156	}
157	printf("=== END OF MACROS ===\n");
158	pchk(macro0.c, &p.c, "macro0", 0);
159	return macrop.c;
160}
161
162static const char *
163dump_macro(union ptr p, int32_t im)
164{
165	union ptr	 page0, pagep;
166	const char	*val0, *valp;
167	int32_t		 i, nentries;
168
169	nentries = be32toh(*p.i++);
170	printf("macro %02d entry count %d\n", im, nentries);
171	if (nentries == 0)
172		return p.c;
173	valp = val0 = dbm_get(p.i[0]);
174	pagep.i = page0.i = dbm_get(p.i[1]);
175	printf("=== MACRO %02d ===\n", im);
176	for (i = 0; i < nentries; i++) {
177		pchk(dbm_get(*p.i++), &valp, "value", 0);
178		printf("macro %02d # ", im);
179		dump_str(&valp);
180		pchk(dbm_get(*p.i++), &pagep.c, "pages", 0);
181		while (*pagep.i++ != 0)
182			printf("# %s ", (char *)dbm_get(
183			    *(int32_t *)dbm_get(pagep.i[-1])) + 1);
184		printf("\n");
185	}
186	printf("=== END OF MACRO %02d ===\n", im);
187	pchk(val0, &p.c, "value0", 0);
188	pchk(page0.c, &valp, "page0", 3);
189	return pagep.c;
190}
191
192static void
193dump_str(const char **cp)
194{
195	if (*cp == NULL) {
196		printf("(NULL)");
197		return;
198	}
199	if ((unsigned char)**cp <= NAME_MASK) {
200		putchar('[');
201		if (**cp & NAME_FILE)
202			putchar('f');
203		if (**cp & NAME_HEAD)
204			putchar('h');
205		if (**cp & NAME_FIRST)
206			putchar('1');
207		if (**cp & NAME_TITLE)
208			putchar('t');
209		if (**cp & NAME_SYN)
210			putchar('s');
211		putchar(']');
212		(*cp)++;
213	}
214	while (**cp != '\0')
215		putchar(*(*cp)++);
216	putchar(' ');
217	(*cp)++;
218}
219
220static void
221dump_lst(const char **cp)
222{
223	if (*cp == NULL) {
224		printf("# (NULL)\n");
225		return;
226	}
227	while (**cp != '\0') {
228		printf("# ");
229		dump_str(cp);
230	}
231	(*cp)++;
232	printf("\n");
233}
234
235static void
236pchk(const char *want, const char **got, const char *name, int fuzz)
237{
238	if (want == NULL) {
239		warnx("%s wants (NULL), ignoring", name);
240		return;
241	}
242	if (*got == NULL)
243		warnx("%s jumps from (NULL) to 0x%x", name,
244		    be32toh(dbm_addr(want)));
245	else if (*got > want || *got + fuzz < want)
246		warnx("%s jumps from 0x%x to 0x%x", name,
247		    be32toh(dbm_addr(*got)), be32toh(dbm_addr(want)));
248	*got = want;
249}
250