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 
30 union ptr {
31 	const char	*c;
32 	const int32_t	*i;
33 };
34 
35 static void		 dump(void);
36 static const char	*dump_macro(union ptr, int32_t);
37 static const char	*dump_macros(union ptr);
38 static const char	*dump_pages(union ptr);
39 static void		 dump_str(const char **);
40 static void		 dump_lst(const char **);
41 static void		 pchk(const char *, const char **, const char *, int);
42 
43 
44 int
main(int argc,char * argv[])45 main(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 
56 static void
dump(void)57 dump(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 
75 static const char *
dump_pages(union ptr p)76 dump_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 
141 static const char *
dump_macros(union ptr p)142 dump_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 
162 static const char *
dump_macro(union ptr p,int32_t im)163 dump_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 
192 static void
dump_str(const char ** cp)193 dump_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 
220 static void
dump_lst(const char ** cp)221 dump_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 
235 static void
pchk(const char * want,const char ** got,const char * name,int fuzz)236 pchk(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