1    	/*
2    	 * Copyright (C) 2018-2021 Red Hat, Inc.  All rights reserved.
3    	 *
4    	 * Author: Christine Caulfield <ccaulfie@redhat.com>
5    	 *
6    	 * This software licensed under GPL-2.0+
7    	 */
8    	
9    	
10   	/*
11   	 * NOTE: this code is very rough, it does the bare minimum to parse the
12   	 * XML out from doxygen and is probably very fragile to changes in that XML
13   	 * schema. It probably leaks memory all over the place too.
14   	 *
15   	 * In its favour, it *does* generate nice man pages and should only be run very ocasionally
16   	 */
17   	
18   	#define _DEFAULT_SOURCE
19   	#define _BSD_SOURCE
20   	#define _GNU_SOURCE
21   	#define _XOPEN_SOURCE
22   	#define _XOPEN_SOURCE_EXTENDED
23   	#define _XPG4_2
24   	#define _XPG7
25   	#include <stdlib.h>
26   	#include <sys/time.h>
27   	#include <sys/stat.h>
28   	#include <time.h>
29   	#include <stdio.h>
30   	#include <limits.h>
31   	#include <string.h>
32   	#include <getopt.h>
33   	#include <errno.h>
34   	#include <ctype.h>
35   	#include <libxml/tree.h>
36   	#include <libxml/parser.h>
37   	#include <qb/qblist.h>
38   	#include <qb/qbmap.h>
39   	#include "cstring.h"
40   	
41   	/*
42   	 * This isn't a maximum size, it just defines how long a parameter
43   	 * type can get before we decide it's not worth lining everything up.
44   	 * It's mainly to stop function pointer types (which can get VERY long because
45   	 * of all *their* parameters) making everything else 'line-up' over separate lines
46   	 */
47   	#define LINE_LENGTH 80
48   	
49   	/* Similar - but for structure member comments */
50   	#define STRUCT_COMMENT_LENGTH 50
51   	
52   	static int print_ascii = 1;
53   	static int print_man = 0;
54   	static int print_params = 0;
55   	static int print_general = 0;
56   	static int num_functions = 0;
57   	static int quiet = 0;
58   	static int use_header_copyright = 0;
59   	static const char *man_section="3";
60   	static const char *package_name="Package";
61   	static const char *header="Programmer's Manual";
62   	static const char *company="Red Hat";
63   	static const char *output_dir="./";
64   	static const char *xml_dir = "./xml/";
65   	static const char *xml_file;
66   	static const char *manpage_date = NULL;
67   	static const char *headerfile = NULL;
68   	static const char *header_prefix = "";
69   	static const char *header_src_dir = "./";
70   	static char header_copyright[256] = "\0";
71   	static long manpage_year = LONG_MIN;
72   	static long start_year = 2010;
73   	static struct qb_list_head params_list;
74   	static struct qb_list_head retval_list;
75   	static qb_map_t *function_map;
76   	static qb_map_t *structures_map;
77   	static qb_map_t *used_structures_map;
78   	
79   	struct param_info {
80   		char *paramname;
81   		char *paramtype;
82   		char *paramdesc;
83   		struct param_info *next;
84   		struct qb_list_head list;
85   	};
86   	
87   	struct struct_info {
88   		enum {STRUCTINFO_STRUCT, STRUCTINFO_ENUM} kind;
89   		char *structname;
90   		char *description;
91   		char *brief_description;
92   		struct qb_list_head params_list; /* our params */
93   		struct qb_list_head list;
94   	};
95   	
96   	static cstring_t get_texttree(int *type, xmlNode *cur_node, char **returntext, char **notetext, int add_nl);
97   	static void traverse_node(xmlNode *parentnode, const char *leafname, void (do_members(xmlNode*, void*)), void *arg);
98   	static cstring_t get_text(xmlNode *cur_node, char **returntext, char **notetext);
99   	static void man_print_long_string(FILE *manfile, char *text);
100  	
101  	static void free_paraminfo(struct param_info *pi)
102  	{
103  		free(pi->paramname);
104  		free(pi->paramtype);
105  		free(pi->paramdesc);
106  		free(pi);
107  	}
108  	
109  	static char *get_attr(xmlNode *node, const char *tag)
110  	{
111  		xmlAttr *this_attr;
112  	
113  		for (this_attr = node->properties; this_attr; this_attr = this_attr->next) {
114  			if (this_attr->type == XML_ATTRIBUTE_NODE && strcmp((char *)this_attr->name, tag) == 0) {
115  				return strdup((char *)this_attr->children->content);
116  			}
117  		}
118  		return NULL;
119  	}
120  	
121  	static cstring_t get_child(xmlNode *node, const char *tag)
122  	{
123  	        xmlNode *this_node;
124  	        xmlNode *child;
125  	        cstring_t buffer = cstring_alloc();
126  		char *refid = NULL;
127  		char *declname = NULL;
128  	
(13) Event example_checked: Example 4: "this_node->next" has its value checked in "this_node".
Also see events: [null_field][example_checked][example_checked][example_checked][example_checked][dereference]
129  		for (this_node = node->children; this_node; this_node = this_node->next) {
130  			if ((strcmp( (char*)this_node->name, "declname") == 0)) {
131  				declname = strdup((char*)this_node->children->content);
132  			}
133  	
134  			if ((this_node->type == XML_ELEMENT_NODE && this_node->children) && ((strcmp((char *)this_node->name, tag) == 0))) {
135  				refid = NULL;
136  				for (child = this_node->children; child; child = child->next) {
137  					if (child->content) {
138  						buffer = cstring_append_chars(buffer, (char *)child->content);
139  					}
140  	
141  					if ((strcmp( (char*)child->name, "ref") == 0)) {
142  						if (child->children->content) {
143  							buffer = cstring_append_chars(buffer, (char *)child->children->content);
144  						}
145  						refid = get_attr(child, "refid");
146  					}
147  				}
148  			}
149  			if (declname && refid) {
150  				qb_map_put(used_structures_map, refid, declname);
151  			}
152  		}
153  		return buffer;
154  	}
155  	
156  	static struct param_info *find_param_by_name(struct qb_list_head *list, const char *name)
157  	{
158  		struct qb_list_head *iter;
159  		struct param_info *pi;
160  	
161  		qb_list_for_each(iter, list) {
162  			pi = qb_list_entry(iter, struct param_info, list);
163  			if (strcmp(pi->paramname, name) == 0) {
164  				return pi;
165  			}
166  		}
167  		return NULL;
168  	}
169  	
170  	static int not_all_whitespace(char *string)
171  	{
172  		unsigned int i;
173  	
174  		for (i=0; i<strlen(string); i++) {
175  			if (string[i] != ' ' &&
176  			    string[i] != '\n' &&
177  			    string[i] != '\r' &&
178  			    string[i] != '\t')
179  				return 1;
180  		}
181  		return 0;
182  	}
183  	
184  	static void get_param_info(xmlNode *cur_node, struct qb_list_head *list)
185  	{
186  		xmlNode *this_tag;
187  		xmlNode *sub_tag;
188  		char *paramname = NULL;
189  		char *paramdesc = NULL;
190  		struct param_info *pi;
191  	
192  		/* This is not robust, and very inflexible */
(1) Event cond_true: Condition "this_tag", taking true branch.
193  		for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) {
(2) Event cond_true: Condition "sub_tag", taking true branch.
194  			for (sub_tag = this_tag->children; sub_tag; sub_tag = sub_tag->next) {
(3) Event cond_true: Condition "sub_tag->type == XML_ELEMENT_NODE", taking true branch.
(4) Event cond_true: Condition "strcmp((char *)sub_tag->name, "parameternamelist") == 0", taking true branch.
(5) Event cond_true: Condition "sub_tag->children->next->children", taking true branch.
195  				if (sub_tag->type == XML_ELEMENT_NODE && strcmp((char *)sub_tag->name, "parameternamelist") == 0 &&
196  					sub_tag->children->next->children) {
197  					paramname = (char*)sub_tag->children->next->children->content;
198  				}
(6) Event cond_true: Condition "sub_tag->type == XML_ELEMENT_NODE", taking true branch.
(7) Event cond_true: Condition "strcmp((char *)sub_tag->name, "parameterdescription") == 0", taking true branch.
(8) Event cond_true: Condition "paramname", taking true branch.
(9) Event null_field: Reading field "next", which is expected to possibly be "NULL" in "sub_tag->children->next" (checked 17 out of 21 times).
(15) Event dereference: Dereferencing "sub_tag->children->next", which is known to be "NULL".
Also see events: [example_checked][example_checked][example_checked][example_checked][example_checked]
199  				if (sub_tag->type == XML_ELEMENT_NODE &&
200  				    strcmp((char *)sub_tag->name, "parameterdescription") == 0 &&
201  				    paramname && sub_tag->children->next->children) {
202  					cstring_t paramdesc_c = get_text(sub_tag->children->next, NULL, NULL);
203  					paramdesc = cstring_to_chars(paramdesc_c);
204  					free(paramdesc_c);
205  	
206  					/* Add text to the param_map */
207  					pi = find_param_by_name(list, paramname);
208  					if (pi) {
209  						pi->paramdesc = paramdesc;
210  					}
211  					else {
212  						pi = malloc(sizeof(struct param_info));
213  						if (pi) {
214  							pi->paramname = paramname;
215  							pi->paramdesc = paramdesc;
216  							pi->paramtype = NULL; /* it's a retval */
217  							qb_list_add_tail(&pi->list, list);
218  						}
219  					}
220  				}
221  			}
222  		}
223  	}
224  	
225  	static cstring_t get_codeline(xmlNode *this_tag)
226  	{
227  		cstring_t buffer = cstring_alloc();
228  		xmlNode *sub_tag;
229  	
(14) Event example_checked: Example 5: "sub_tag->next" has its value checked in "sub_tag".
Also see events: [null_field][example_checked][example_checked][example_checked][example_checked][dereference]
230  		for (sub_tag = this_tag; sub_tag; sub_tag = sub_tag->next) {
231  			if (strcmp((char*)sub_tag->name, "sp") == 0) {
232  				buffer = cstring_append_chars(buffer, " ");
233  			}
234  			if (strcmp((char*)sub_tag->name, "text") == 0) {
235  				// If the line starts with a dot then escape the first one to
236  				// stop nroff thinking it's a macro
237  				char *tmp = (char*)sub_tag->content;
238  				if (tmp[0] == '.') {
239  					buffer = cstring_append_chars(buffer, (char*)"\\[char46]");
240  					tmp += 1;
241  				}
242  				buffer = cstring_append_chars(buffer, tmp);
243  			}
244  			if (strcmp((char*)sub_tag->name, "ref") == 0) {
245  				// Handled by the child recusion below
246  			}
247  			if (sub_tag->children) {
248  				char *tmp = get_codeline(sub_tag->children);
249  				buffer = cstring_append_cstring(buffer, tmp);
250  				cstring_free(tmp);
251  			}
252  		}
253  		return buffer;
254  	}
255  	
256  	static cstring_t get_codetree(xmlNode *cur_node)
257  	{
258  		xmlNode *this_tag;
259  		cstring_t buffer = cstring_alloc();
260  		cstring_t tmp;
261  	
262  		if (print_man) {
263  			buffer = cstring_append_chars(buffer, "\n.nf\n");
264  		}
265  	
266  		for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) {
267  			if (strcmp((char*)this_tag->name, "codeline") == 0) {
268  	
269  				tmp = get_codeline(this_tag->children);
270  				buffer = cstring_append_cstring(buffer, tmp);
271  				cstring_free(tmp);
272  			}
273  			if (strcmp((char*)this_tag->name, "text") == 0) {
274  				buffer = cstring_append_chars(buffer, (char*)this_tag->content);
275  			}
276  		}
277  	
278  		if (print_man) {
279  			buffer = cstring_append_chars(buffer, ".fi\n");
280  		}
281  	
282  		return buffer;
283  	}
284  	
285  	
286  	static cstring_t get_text(xmlNode *cur_node, char **returntext, char **notetext)
287  	{
288  		xmlNode *this_tag;
289  		xmlNode *sub_tag;
290  		char *kind;
291  		cstring_t buffer = cstring_alloc();
292  	
293  		for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) {
294  			if (this_tag->type == XML_TEXT_NODE && strcmp((char *)this_tag->name, "text") == 0) {
295  				if (not_all_whitespace((char*)this_tag->content)) {
296  					buffer = cstring_append_chars(buffer, (char*)this_tag->content);
297  				}
298  			}
299  			if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "emphasis") == 0) {
300  				if (print_man) {
301  					buffer = cstring_append_chars(buffer, "\\fB");
302  				}
303  				buffer = cstring_append_chars(buffer, (char*)this_tag->children->content);
304  				if (print_man) {
305  					buffer = cstring_append_chars(buffer, "\\fR");
306  				}
307  			}
308  	
309  			if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "ref") == 0) {
310  				if (print_man) {
311  					buffer = cstring_append_chars(buffer, "\\fI");
312  				}
313  				buffer = cstring_append_chars(buffer, (char*)this_tag->children->content);
314  				if (print_man) {
315  					buffer = cstring_append_chars(buffer, "\\fR");
316  				}
317  			}
318  			if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "computeroutput") == 0) {
319  				if (print_man) {
320  					buffer = cstring_append_chars(buffer, "\\fB");
321  				}
322  				buffer = cstring_append_chars(buffer, (char*)this_tag->children->content);
323  				if (print_man) {
324  					buffer = cstring_append_chars(buffer, "\\fP");
325  				}
326  			}
327  	
328  			if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "itemizedlist") == 0) {
329  				for (sub_tag = this_tag->children; sub_tag; sub_tag = sub_tag->next) {
330  					if (sub_tag->type == XML_ELEMENT_NODE && strcmp((char *)sub_tag->name, "listitem") == 0
331  					    && sub_tag->children->children->content) {
332  						buffer = cstring_append_chars(buffer, (char*)sub_tag->children->children->content);
333  						buffer = cstring_append_chars(buffer, "\n");
334  					}
335  				}
336  			}
337  	
338  			if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "programlisting") == 0) {
339  				cstring_t tmp = get_codetree(this_tag);
340  				buffer = cstring_append_cstring(buffer, tmp);
341  				buffer = cstring_append_chars(buffer, "\n");
342  				cstring_free(tmp);
343  			}
344  	
345  			/* Look for subsections - return value & params */
346  			if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "simplesect") == 0) {
347  				cstring_t tmp;
348  	
349  				kind = get_attr(this_tag, "kind");
350  				tmp = get_text(this_tag->children, NULL, NULL);
351  	
352  				if (returntext && strcmp(kind, "return") == 0) {
353  					*returntext = cstring_to_chars(tmp);
354  				}
355  				if (notetext && strcmp(kind, "note") == 0) {
356  					*notetext = cstring_to_chars(tmp);
357  				}
358  				if (notetext && strcmp(kind, "par") == 0) {
359  					int type;
360  	
361  					tmp = get_child(this_tag, "title");
362  					buffer = cstring_append_cstring(buffer, tmp);
363  					buffer = cstring_append_chars(buffer, "\n");
364  					cstring_free(tmp);
365  	
366  					tmp = get_texttree(&type,this_tag, NULL, NULL, 1);
367  					buffer = cstring_append_cstring(buffer, tmp);
368  					buffer = cstring_append_chars(buffer, "\n");
369  				}
370  				cstring_free(tmp);
371  			}
372  	
373  			if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "parameterlist") == 0) {
374  				kind = get_attr(this_tag, "kind");
375  				if (strcmp(kind, "param") == 0) {
376  					get_param_info(this_tag, &params_list);
377  				}
378  				if (strcmp(kind, "retval") == 0) {
379  					get_param_info(this_tag, &retval_list);
380  				}
381  			}
382  		}
383  		return buffer;
384  	}
385  	
386  	static void read_structname(xmlNode *cur_node, void *arg)
387  	{
388  		struct struct_info *si=arg;
389  		xmlNode *this_tag;
390  	
391  		for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) {
392  			if (strcmp((char*)this_tag->name, "compoundname") == 0) {
393  				si->structname = strdup((char*)this_tag->children->content);
394  			}
395  		}
396  	}
397  	
398  	static void read_structdesc(xmlNode *cur_node, void *arg)
399  	{
400  		struct struct_info *si=arg;
401  		xmlNode *this_tag;
402  	
403  		for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) {
404  			if (strcmp((char*)this_tag->name, "detaileddescription") == 0) {
405  				cstring_t desc = get_texttree(NULL, this_tag, NULL, NULL, 1);
406  				si->description = cstring_to_chars(desc);
407  				cstring_free(desc);
408  			}
409  			if (strcmp((char*)this_tag->name, "briefdescription") == 0) {
410  				cstring_t brief = get_texttree(NULL, this_tag, NULL, NULL, 1);
411  				si->brief_description = cstring_to_chars(brief);
412  			}
413  		}
414  	}
415  	
416  	
417  	static void read_headername(xmlNode *cur_node, void *arg)
418  	{
419  		char **h_file = arg;
420  		xmlNode *this_tag;
421  	
422  		for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) {
423  			if (strcmp((char*)this_tag->name, "compoundname") == 0) {
424  				*h_file = strdup((char*)this_tag->children->content);
425  			}
426  		}
427  	}
428  	
429  	
430  	/* Called from traverse_node() */
431  	static void read_struct(xmlNode *cur_node, void *arg)
432  	{
433  		xmlNode *this_tag;
434  		struct struct_info *si=arg;
435  		struct param_info *pi = NULL;
436  		char fullname[1024];
437  		char *type = NULL;
438  		char *name = NULL;
439  		char *desc = NULL;
440  		const char *args="";
441  	
442  		for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) {
443  			if (strcmp((char*)this_tag->name, "type") == 0) {
444  				type = (char*)this_tag->children->content;
445  				/* If type is NULL then look for a ref - it's probably an external struct or typedef */
446  				if (type == NULL) {
447  					cstring_t tmp = get_child(this_tag, "ref");
448  					type = cstring_to_chars(tmp);
449  					cstring_free(tmp);
450  				}
451  			}
452  			if (strcmp((char*)this_tag->name, "name") == 0) {
453  				name = (char*)this_tag->children->content;
454  			}
455  			if (this_tag->children && strcmp((char*)this_tag->name, "argsstring") == 0) {
456  				args = (char*)this_tag->children->content;
457  			}
458  			if (this_tag->children && strcmp((char*)this_tag->name, "detaileddescription") == 0) {
459  				cstring_t *desc_cs = get_texttree(NULL, this_tag, NULL, NULL, 0);
460  				if (cstring_len(desc_cs) > 1) {
461  					desc = cstring_to_chars(desc_cs);
462  				}
463  				cstring_free(desc_cs);
464  			}
465  		}
466  	
467  		if (name) {
468  			pi = malloc(sizeof(struct param_info));
469  			if (pi) {
470  				snprintf(fullname, sizeof(fullname), "%s%s", name, args);
471  				pi->paramtype = type?strdup(type):strdup("");
472  				pi->paramname = strdup(fullname);
473  				pi->paramdesc = desc;
474  				qb_list_add_tail(&pi->list, &si->params_list);
475  			}
476  		}
477  		/* Tidy */
478  		if (!name || !pi) {
479  			free(desc);
480  		}
481  	}
482  	
483  	static int read_structure_from_xml(const char *refid, const char *name)
484  	{
485  		char fname[PATH_MAX];
486  		xmlNode *rootdoc;
487  		xmlDocPtr doc;
488  		struct struct_info *si;
489  		struct stat st;
490  		int ret = -1;
491  	
492  		snprintf(fname, sizeof(fname),  "%s/%s.xml", xml_dir, refid);
493  	
494  		/* Don't call into libxml if the file does not exist - saves unwanted error messages */
495  		if (stat(fname, &st) == -1) {
496  			return -1;
497  		}
498  	
499  		doc = xmlParseFile(fname);
500  		if (doc == NULL) {
501  			fprintf(stderr, "Error: unable to open xml file for %s\n", refid);
502  			return -1;
503  		}
504  	
505  		rootdoc = xmlDocGetRootElement(doc);
506  		if (!rootdoc) {
507  			fprintf(stderr, "Can't find \"document root\"\n");
508  			return -1;
509  		}
510  	
511  		si = malloc(sizeof(struct struct_info));
512  		if (si) {
513  			memset(si, 0, sizeof(*si));
514  			si->kind = STRUCTINFO_STRUCT;
515  			qb_list_init(&si->params_list);
516  			traverse_node(rootdoc, "memberdef", read_struct, si);
517  			traverse_node(rootdoc, "compounddef", read_structdesc, si);
518  			traverse_node(rootdoc, "compounddef", read_structname, si);
519  			ret = 0;
520  			qb_map_put(structures_map, refid, si);
521  		}
522  		xmlFreeDoc(doc);
523  	
524  		return ret;
525  	}
526  	
527  	static char *allcaps(const char *name)
528  	{
529  		static char buffer[4096] = {'\0'};
530  		size_t i;
531  	
532  		if (name) {
533  			size_t len = strnlen(name, 4096);
534  			for (i=0; i< len; i++) {
535  				buffer[i] = toupper(name[i]);
536  			}
537  			buffer[len] = '\0';
538  		}
539  		return buffer;
540  	}
541  	
542  	/*
543  	 * Print a structure comment that would be too long
544  	 * to fit after the structure member, in a style ...
545  	 * well, in a style like this!
546  	 */
547  	static void print_long_structure_comment(FILE *manfile, char *struct_comment)
548  	{
549  		int column = 7;
550  		char *comment = strdup(struct_comment); /* We're using strdup */
551  		char *ptr = strtok(comment, " ");
552  	
553  		fprintf(manfile, "\\fP    /*");
554  		fprintf(manfile, "\n     *");
555  		while (ptr) {
556  			column += strlen(ptr)+1;
557  			if (column > 80) {
558  				fprintf(manfile, "\n     *");
559  				column = 7;
560  			}
561  			fprintf(manfile, " %s", ptr);
562  			ptr = strtok(NULL, " ");
563  		}
564  		fprintf(manfile, "\n     */\n");
565  		free(comment);
566  	}
567  	
568  	static void print_param(FILE *manfile, struct param_info *pi, int type_field_width, int name_field_width, int bold, const char *delimiter)
569  	{
570  		const char *asterisks = "  ";
571  		char *type = pi->paramtype;
572  		int typelength = strlen(type);
573  	
574  		/* Reformat pointer params so they look nicer */
575  		if (typelength > 0 && pi->paramtype[typelength-1] == '*') {
576  			asterisks=" *";
577  			type = strdup(pi->paramtype);
578  			type[typelength-1] = '\0';
579  	
580  			/* Cope with double pointers */
581  			if (typelength > 1 && pi->paramtype[typelength-2] == '*') {
582  				asterisks="**";
583  				type[typelength-2] = '\0';
584  			}
585  	
586  			/* Tidy function pointers */
587  			if (typelength > 1 && pi->paramtype[typelength-2] == '(') {
588  				asterisks="(*";
589  				type[typelength-2] = '\0';
590  			}
591  		}
592  	
593  		/* Print structure description if available */
594  		if (pi->paramdesc) {
595  			/* Too long to go on the same line? */
596  			if (strlen(pi->paramdesc) > STRUCT_COMMENT_LENGTH) {
597  				print_long_structure_comment(manfile, pi->paramdesc);
598  				fprintf(manfile, "    %s%-*s%s%s\\fI%s\\fP%s\n",
599  					bold?"\\fB":"", type_field_width, type,
600  					asterisks, bold?"\\fP":"",
601  					pi->paramname?pi->paramname:"", delimiter);
602  			} else {
603  				/* Pad out so they all line up */
604  				int pad_length = (name_field_width+2) -
605  					(pi->paramname?strlen(pi->paramname):0) - strlen(delimiter) + 1;
606  				fprintf(manfile, "    %s%-*s%s%s\\fI%s\\fP%s\\fR%*s/* %s*/\n",
607  					bold?"\\fB":"", type_field_width, type,
608  					asterisks, bold?"\\fP":"",
609  					pi->paramname?pi->paramname:"", delimiter,
610  					pad_length, " ",
611  					pi->paramdesc);
612  			}
613  		} else {
614  			fprintf(manfile, "    %s%-*s%s%s\\fI%s\\fP%s\n",
615  				bold?"\\fB":"", type_field_width, type,
616  				asterisks, bold?"\\fP":"",
617  				pi->paramname?pi->paramname:"", delimiter);
618  		}
619  	
620  		if (type != pi->paramtype) {
621  			free(type);
622  		}
623  	}
624  	
625  	static void print_structure(FILE *manfile, struct struct_info *si)
626  	{
627  		struct param_info *pi;
628  		struct qb_list_head *iter;
629  		unsigned int max_param_length=0;
630  		unsigned int max_param_name_length=0;
631  	
632  		fprintf(manfile, ".nf\n");
633  	
634  		if (si->brief_description) {
635  			fprintf(manfile, "%s\n", si->brief_description);
636  		}
637  		if (si->description) {
638  			fprintf(manfile, "%s\n", si->description);
639  		}
640  	
641  		qb_list_for_each(iter, &si->params_list) {
642  			pi = qb_list_entry(iter, struct param_info, list);
643  			if (strlen(pi->paramtype) > max_param_length) {
644  				max_param_length = strlen(pi->paramtype);
645  			}
646  			if (strlen(pi->paramname) > max_param_name_length) {
647  				max_param_name_length = strlen(pi->paramname);
648  			}
649  		}
650  	
651  		fprintf(manfile, "\\fB\n");
652  		if (si->kind == STRUCTINFO_STRUCT) {
653  			fprintf(manfile, "struct %s {\n", si->structname);
654  		} else if (si->kind == STRUCTINFO_ENUM) {
655  			fprintf(manfile, "enum %s {\n", si->structname);
656  		} else {
657  			fprintf(manfile, "%s {\n", si->structname);
658  		}
659  		fprintf(manfile, "\\fR\n");
660  	
661  		qb_list_for_each(iter, &si->params_list) {
662  			fprintf(manfile, "\\fB\n");
663  			pi = qb_list_entry(iter, struct param_info, list);
664  			print_param(manfile, pi, max_param_length, max_param_name_length, 1, ";");
665  		}
666  		fprintf(manfile, "};\n");
667  	
668  		fprintf(manfile, "\\fP\n");
669  		fprintf(manfile, ".fi\n");
670  	}
671  	
672  	cstring_t get_texttree(int *type, xmlNode *cur_node, char **returntext, char **notetext, int add_nl)
673  	{
674  		xmlNode *this_tag;
675  		cstring_t tmp;
676  		cstring_t buffer = cstring_alloc();
677  	
678  		for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) {
679  	
680  			if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "para") == 0) {
681  				tmp = get_text(this_tag, returntext, notetext);
682  				buffer = cstring_append_cstring(buffer, tmp);
683  				if (add_nl) {
684  					buffer = cstring_append_chars(buffer, "\n");
685  				}
686  	
687  				cstring_free(tmp);
688  			}
689  		}
690  		return buffer;
691  	}
692  	
693  	/* The text output is VERY basic and just a check that it's working really */
694  	static void print_text(char *name, char *def, char *brief, char *args, char *detailed,
695  			       struct qb_list_head *param_list, char *returntext, char *notetext)
696  	{
697  		printf(" ------------------ %s --------------------\n", name);
698  		printf("NAME\n");
699  		if (brief) {
700  			printf("        %s - %s\n", name, brief);
701  		} else {
702  			printf("        %s\n", name);
703  		}
704  	
705  		printf("SYNOPSIS\n");
706  		printf("        #include <%s%s>\n", header_prefix, headerfile);
707  		if (args) {
708  			printf("        %s %s\n\n", name, args);
709  		}
710  	
711  		if (detailed) {
712  			printf("DESCRIPTION\n");
713  			printf("        %s\n", detailed);
714  		}
715  	
716  		if (returntext) {
717  			printf("RETURN VALUE\n");
718  			printf("        %s\n", returntext);
719  		}
720  		if (notetext) {
721  			printf("NOTE\n");
722  			printf("        %s\n", notetext);
723  		}
724  	}
725  	
726  	/* Print a long string with para marks in it. */
727  	static void man_print_long_string(FILE *manfile, char *text)
728  	{
729  		char *next_nl;
730  		char *current = text;
731  		int in_prog = 0;
732  	
733  		next_nl = strchr(text, '\n');
734  		while (next_nl && *next_nl != '\0') {
735  			*next_nl = '\0';
736  	
737  			// Don't format @code blocks
738  			if (strncmp(current, ".nf", 3) == 0) {
739  				in_prog = 1;
740  				fprintf(manfile, "\n");
741  			}
742  	
743  			if (in_prog) {
744  				fprintf(manfile, "%s\n", current);
745  			} else {
746  				if (strlen(current)) {
747  					fprintf(manfile, ".PP\n%s\n", current);
748  				}
749  			}
750  	
751  			if (strncmp(current, ".fi", 3) == 0) {
752  				in_prog = 0;
753  				fprintf(manfile, "\n");
754  			}
755  	
756  			*next_nl = '\n';
757  			current = next_nl+1;
758  			next_nl = strchr(current, '\n');
759  		}
760  	
761  		/* The bit at the end */
762  		if (strlen(current) && !in_prog) {
763  			fprintf(manfile, ".PP\n%s\n", current);
764  		}
765  	}
766  	
767  	static void print_manpage(char *name, char *def, char *brief, char *args, char *detailed,
768  				  struct qb_list_head *param_map, char *returntext, char *notetext)
769  	{
770  		char manfilename[PATH_MAX];
771  		char gendate[64];
772  		const char *dateptr = gendate;
773  		FILE *manfile;
774  		time_t t;
775  		struct tm *tm;
776  		qb_map_iter_t *map_iter;
777  		struct qb_list_head *iter;
778  		struct qb_list_head *tmp;
779  		const char *p;
780  		void *data;
781  		unsigned int max_param_type_len;
782  		unsigned int max_param_name_len;
783  		unsigned int num_param_descs;
784  		int param_count = 0;
785  		int param_num = 0;
786  		struct param_info *pi;
787  	
788  		t = time(NULL);
789  		tm = localtime(&t);
790  		if (!tm) {
791  			perror("unable to get localtime");
792  			exit(1);
793  		}
794  		strftime(gendate, sizeof(gendate), "%Y-%m-%d", tm);
795  	
796  		if (manpage_date) {
797  			dateptr = manpage_date;
798  		}
799  		if (manpage_year == LONG_MIN) {
800  			manpage_year = tm->tm_year+1900;
801  		}
802  	
803  		snprintf(manfilename, sizeof(manfilename), "%s/%s.%s", output_dir, name, man_section);
804  		manfile = fopen(manfilename, "w+");
805  		if (!manfile) {
806  			perror("unable to open output file");
807  			printf("%s", manfilename);
808  			exit(1);
809  		}
810  	
811  		/* Work out the length of the parameters, so we can line them up   */
812  		max_param_type_len = 0;
813  		max_param_name_len = 0;
814  		num_param_descs = 0;
815  	
816  		qb_list_for_each(iter, param_map) {
817  			pi = qb_list_entry(iter, struct param_info, list);
818  	
819  			/* It's mainly macros that break this,
820  			 * macros need more work
821  			 */
822  			if (!pi->paramtype) {
823  				pi->paramtype = strdup("");
824  			}
825  			if ((strlen(pi->paramtype) < LINE_LENGTH) &&
826  			    (strlen(pi->paramtype) > max_param_type_len)) {
827  				max_param_type_len = strlen(pi->paramtype);
828  			}
829  			if (strlen(pi->paramname) > max_param_name_len) {
830  				max_param_name_len = strlen(pi->paramname);
831  			}
832  			if (pi->paramdesc && pi->paramtype[0] != '\0') {
833  				num_param_descs++;
834  			}
835  			param_count++;
836  		}
837  	
838  		/* Off we go */
839  	
840  		fprintf(manfile, ".\\\"  Automatically generated man page, do not edit\n");
841  		fprintf(manfile, ".TH %s %s %s \"%s\" \"%s\"\n", allcaps(name), man_section, dateptr, package_name, header);
842  	
843  		fprintf(manfile, ".SH NAME\n");
844  		if (brief && not_all_whitespace(brief)) {
845  			fprintf(manfile, "%s \\- %s\n", name, brief);
846  		} else {
847  			fprintf(manfile, "%s\n", name);
848  		}
849  	
850  		fprintf(manfile, ".SH SYNOPSIS\n");
851  		fprintf(manfile, ".nf\n");
852  		fprintf(manfile, ".B #include <%s%s>\n", header_prefix, headerfile);
853  		if (def) {
854  			fprintf(manfile, ".sp\n");
855  			fprintf(manfile, "\\fB%s\\fP(\n", def);
856  	
857  			qb_list_for_each(iter, param_map) {
858  				pi = qb_list_entry(iter, struct param_info, list);
859  	
860  				if (pi->paramtype[0] != '\0') {
861  					print_param(manfile, pi, max_param_type_len, max_param_name_len, 1, ++param_num < param_count?",":"");
862  				}
863  			}
864  	
865  			fprintf(manfile, ");\n");
866  			fprintf(manfile, ".fi\n");
867  		}
868  	
869  		if (print_params && num_param_descs) {
870  			fprintf(manfile, ".SH PARAMS\n");
871  	
872  			qb_list_for_each(iter, &params_list) {
873  				pi = qb_list_entry(iter, struct param_info, list);
874  				fprintf(manfile, "\\fB%-*s \\fP\\fI%s\\fP\n", (int)max_param_name_len, pi->paramname,
875  					pi->paramdesc);
876  				fprintf(manfile, ".PP\n");
877  			}
878  		}
879  	
880  		if (detailed) {
881  			fprintf(manfile, ".SH DESCRIPTION\n");
882  			man_print_long_string(manfile, detailed);
883  		}
884  	
885  		if (qb_map_count_get(used_structures_map)) {
886  			int first_struct = 1;
887  	
888  			map_iter = qb_map_iter_create(used_structures_map);
889  			for (p = qb_map_iter_next(map_iter, &data); p; p = qb_map_iter_next(map_iter, &data)) {
890  				struct struct_info *si;
891  				const char *refid = p;
892  				char *refname = data;
893  	
894  				/* If it's not been read in - go and look for it */
895  				si = qb_map_get(structures_map, refid);
896  				if (!si) {
897  					if (!read_structure_from_xml(refid, refname)) {
898  						si = qb_map_get(structures_map, refid);
899  					}
900  				}
901  	
902  				/* Only print header if the struct files exist - sometimes they don't */
903  				if (si && first_struct) {
904  					fprintf(manfile, ".SH STRUCTURES\n");
905  					first_struct = 0;
906  				}
907  				if (si) {
908  					print_structure(manfile, si);
909  					fprintf(manfile, ".PP\n");
910  				}
911  			}
912  			qb_map_iter_free(map_iter);
913  	
914  			fprintf(manfile, ".RE\n");
915  		}
916  	
917  		if (returntext || !qb_list_empty(&retval_list)) {
918  			fprintf(manfile, ".SH RETURN VALUE\n");
919  			if (returntext) {
920  				man_print_long_string(manfile, returntext);
921  			}
922  			fprintf(manfile, ".PP\n");
923  		}
924  	
925  		qb_list_for_each(iter, &retval_list) {
926  			pi = qb_list_entry(iter, struct param_info, list);
927  	
928  			fprintf(manfile, "\\fB%-*s \\fP%s\n", 10, pi->paramname,
929  				pi->paramdesc);
930  			fprintf(manfile, ".PP\n");
931  		}
932  	
933  		if (notetext) {
934  			fprintf(manfile, ".SH NOTE\n");
935  			man_print_long_string(manfile, notetext);
936  		}
937  	
938  		fprintf(manfile, ".SH SEE ALSO\n");
939  		fprintf(manfile, ".PP\n");
940  		fprintf(manfile, ".nh\n");
941  		fprintf(manfile, ".ad l\n");
942  	
943  		param_num = 0;
944  		map_iter = qb_map_iter_create(function_map);
945  		for (p = qb_map_iter_next(map_iter, &data); p; p = qb_map_iter_next(map_iter, &data)) {
946  	
947  			/* Exclude us! */
948  			if (strcmp(data, name)) {
949  				fprintf(manfile, "\\fI%s\\fR(%s)%s", (char *)data, man_section,
950  					param_num < (num_functions - 1)?", ":"");
951  			}
952  			param_num++;
953  		}
954  		qb_map_iter_free(map_iter);
955  	
956  		fprintf(manfile, "\n");
957  		fprintf(manfile, ".ad\n");
958  		fprintf(manfile, ".hy\n");
959  		fprintf(manfile, ".SH \"COPYRIGHT\"\n");
960  		fprintf(manfile, ".PP\n");
961  		if (header_copyright[0] == 'C') {
962  			fprintf(manfile, "%s", header_copyright); /* String already contains trailing NL */
963  		} else {
964  			fprintf(manfile, "Copyright (C) %4ld-%4ld %s, Inc. All rights reserved.\n", start_year, manpage_year, company);
965  		}
966  		fclose(manfile);
967  	
968  		/* Free the params & retval info */
969  		qb_list_for_each_safe(iter, tmp, &params_list) {
970  			pi = qb_list_entry(iter, struct param_info, list);
971  			qb_list_del(&pi->list);
972  			free_paraminfo(pi);
973  		}
974  	
975  		qb_list_for_each_safe(iter, tmp, &retval_list) {
976  			pi = qb_list_entry(iter, struct param_info, list);
977  			qb_list_del(&pi->list);
978  			free_paraminfo(pi);
979  		}
980  	
981  		/* Free used-structures map */
982  		map_iter = qb_map_iter_create(used_structures_map);
983  		for (p = qb_map_iter_next(map_iter, &data); p; p = qb_map_iter_next(map_iter, &data)) {
984  			qb_map_rm(used_structures_map, p);
985  			free(data);
986  		}
987  	}
988  	
989  	/* Same as traverse_members, but to collect function names */
990  	static void collect_functions(xmlNode *cur_node, void *arg)
991  	{
992  		xmlNode *this_tag;
993  		char *kind;
994  		char *name = NULL;
995  	
996  		if (cur_node->name && strcmp((char *)cur_node->name, "memberdef") == 0) {
997  	
998  			kind = get_attr(cur_node, "kind");
999  			if (kind && strcmp(kind, "function") == 0) {
1000 	
(12) Event example_checked: Example 3: "this_tag->next" has its value checked in "this_tag".
Also see events: [null_field][example_checked][example_checked][example_checked][example_checked][dereference]
1001 				for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) {
1002 					if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "name") == 0) {
1003 						name = strdup((char *)this_tag->children->content);
1004 					}
1005 				}
1006 	
1007 				if (name) {
1008 					qb_map_put(function_map, name, name);
1009 					num_functions++;
1010 				}
1011 			}
1012 		}
1013 	}
1014 	
1015 	/* Same as traverse_members, but to collect enums. The behave like structures for,
1016 	   but, for some reason, are in the main XML file rather than their own */
1017 	static void collect_enums(xmlNode *cur_node, void *arg)
1018 	{
1019 		xmlNode *this_tag;
1020 		struct struct_info *si;
1021 		char *kind;
1022 		char *refid = NULL;
1023 		char *name = NULL;
1024 	
1025 		if (cur_node->name && strcmp((char *)cur_node->name, "memberdef") == 0) {
1026 	
1027 			kind = get_attr(cur_node, "kind");
1028 			if (kind && strcmp(kind, "enum") == 0) {
1029 				refid = get_attr(cur_node, "id");
1030 	
(11) Event example_checked: Example 2: "this_tag->next" has its value checked in "this_tag".
Also see events: [null_field][example_checked][example_checked][example_checked][example_checked][dereference]
1031 				for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) {
1032 					if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "name") == 0) {
1033 						name = strdup((char *)this_tag->children->content);
1034 					}
1035 				}
1036 	
1037 				if (name) {
1038 					si = malloc(sizeof(struct struct_info));
1039 					if (si) {
1040 						memset(si, 0, sizeof(*si));
1041 						si->kind = STRUCTINFO_ENUM;
1042 						qb_list_init(&si->params_list);
1043 						si->structname = strdup(name);
1044 						traverse_node(cur_node, "enumvalue", read_struct, si);
1045 						qb_map_put(structures_map, refid, si);
1046 					}
1047 				}
1048 			}
1049 		}
1050 	}
1051 	
1052 	static void traverse_members(xmlNode *cur_node, void *arg)
1053 	{
1054 		xmlNode *this_tag;
1055 	
1056 		qb_list_init(&params_list);
1057 	
1058 		/* if arg == NULL then we're generating a page for the whole header file */
1059 		if ((cur_node->name && (strcmp((char *)cur_node->name, "memberdef") == 0)) ||
1060 		    ((arg == NULL) && cur_node->name && strcmp((char *)cur_node->name, "compounddef")) == 0) {
1061 			char *kind = NULL;
1062 			char *def = NULL;
1063 			char *args = NULL;
1064 			char *name = NULL;
1065 			char *brief = NULL;
1066 			char *detailed = NULL;
1067 			char *returntext = NULL;
1068 			char *notetext = NULL;
1069 			int type;
1070 	
1071 			kind=def=args=name=NULL;
1072 	
1073 			kind = get_attr(cur_node, "kind");
1074 	
1075 			for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next)
1076 			{
1077 				if (!this_tag->children || !this_tag->children->content)
1078 					continue;
1079 	
1080 				if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "definition") == 0)
1081 					def = strdup((char *)this_tag->children->content);
1082 				if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "argsstring") == 0)
1083 					args = strdup((char *)this_tag->children->content);
1084 				if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "name") == 0)
1085 					name = strdup((char *)this_tag->children->content);
1086 	
1087 				if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "briefdescription") == 0) {
1088 					cstring_t tmp = get_texttree(&type, this_tag, &returntext, &notetext, 1);
1089 					if (!brief) {
1090 						brief = cstring_to_chars(tmp);
1091 					} else {
1092 						fprintf(stderr, "ERROR function %s has 2 briefdescription tags\n", name?name:"unknown");
1093 					}
1094 					cstring_free(tmp);
1095 				}
1096 				if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "detaileddescription") == 0) {
1097 					cstring_t tmp = get_texttree(&type, this_tag, &returntext, &notetext, 1);
1098 					if (!detailed) {
1099 						detailed = cstring_to_chars(tmp);
1100 					} else {
1101 						fprintf(stderr, "ERROR function %s has 2 detaileddescription tags\n", name?name:"unknown");
1102 					}
1103 					cstring_free(tmp);
1104 				}
1105 				/* Get all the params */
1106 				if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "param") == 0) {
1107 					cstring_t param_type = get_child(this_tag, "type");
1108 					cstring_t param_name = get_child(this_tag, "declname");
1109 					struct param_info *pi = malloc(sizeof(struct param_info));
1110 					if (pi) {
1111 						pi->paramname = cstring_to_chars(param_name);
1112 						pi->paramtype = cstring_to_chars(param_type);
1113 						pi->paramdesc = NULL;
1114 						qb_list_add_tail(&pi->list, &params_list);
1115 					}
1116 				}
1117 			}
1118 	
1119 			if (arg == headerfile) {
1120 				/* Print header page */
1121 				name = (char*)headerfile;
1122 				if (print_man) {
1123 					if (!quiet) {
1124 						printf("Printing header manpage for %s\n", name);
1125 					}
1126 					print_manpage(name, def, brief, args, detailed, &params_list, returntext, notetext);
1127 				}
1128 				else {
1129 					print_text(name, def, brief, args, detailed, &params_list, returntext, notetext);
1130 				}
1131 			}
1132 	
1133 			if (kind && strcmp(kind, "function") == 0) {
1134 	
1135 				/* Make sure function has a doxygen description */
1136 				if (!detailed) {
1137 					fprintf(stderr, "No detailed description for function '%s' - please fix this\n", name);
1138 				}
1139 	
1140 				if (!name) {
1141 					fprintf(stderr, "Internal error - no name found for function\n");
1142 				} else {
1143 					if (print_man) {
1144 						if (!quiet) {
1145 							printf("Printing manpage for %s\n", name);
1146 						}
1147 						print_manpage(name, def, brief, args, detailed, &params_list, returntext, notetext);
1148 					}
1149 					else {
1150 						print_text(name, def, brief, args, detailed, &params_list, returntext, notetext);
1151 					}
1152 				}
1153 	
1154 			}
1155 	
1156 			free(kind);
1157 			free(def);
1158 			free(args);
1159 			free(name);
1160 			free(brief);
1161 			free(detailed);
1162 		}
1163 	}
1164 	
1165 	
1166 	static void traverse_node(xmlNode *parentnode, const char *leafname, void (do_members(xmlNode*, void*)), void *arg)
1167 	{
1168 		xmlNode *cur_node;
1169 	
(10) Event example_checked: Example 1: "cur_node->next" has its value checked in "cur_node".
Also see events: [null_field][example_checked][example_checked][example_checked][example_checked][dereference]
1170 		for (cur_node = parentnode->children; cur_node; cur_node = cur_node->next) {
1171 			if (cur_node->type == XML_ELEMENT_NODE && cur_node->name
1172 			    && strcmp((char*)cur_node->name, leafname)==0) {
1173 				do_members(cur_node, arg);
1174 				continue;
1175 			}
1176 			if (cur_node->type == XML_ELEMENT_NODE) {
1177 				traverse_node(cur_node, leafname, do_members, arg);
1178 			}
1179 		}
1180 	}
1181 	
1182 	
1183 	static void usage(char *name)
1184 	{
1185 		printf("Usage:\n");
1186 		printf("      %s [OPTIONS] <XML file>\n", name);
1187 		printf("\n");
1188 		printf(" This is a tool to generate API manpages from a doxygen-annotated header file.\n");
1189 		printf(" First run doxygen on the file and then run this program against the main XML file\n");
1190 		printf(" it created and the directory containing the ancilliary files. It will then\n");
1191 		printf(" output a lot of *.3 man page files which you can then ship with your library.\n");
1192 		printf("\n");
1193 		printf(" You will need to invoke this program once for each .h file in your library,\n");
1194 		printf(" using the name of the generated .xml file. This file will usually be called\n");
1195 		printf(" something like <include-file>_8h.xml, eg qbipcs_8h.xml\n");
1196 		printf("\n");
1197 		printf(" If you want HTML output then simply use nroff on the generated files as you\n");
1198 		printf(" would do with any other man page.\n");
1199 		printf("\n");
1200 		printf("       -a            Print ASCII dump of man pages to stdout\n");
1201 		printf("       -m            Write man page files to <output dir>\n");
1202 		printf("       -P            Print PARAMS section\n");
1203 		printf("       -g            Print general man page for the whole header file\n");
1204 		printf("       -c            Use the Copyright date from the header file (if one can be found)\n");
1205 		printf("       -O <dir>      Directory for the original header file. Often needed by -c above\n");
1206 		printf("       -s <s>        Write man pages into section <s> (default: 3)\n");
1207 		printf("       -p <package>  Use <package> name (default: Package)\n");
1208 		printf("       -H <header>   Set header (default: \"Programmer's Manual\"\n");
1209 		printf("       -I <include>  Set include filename (default taken from xml)\n");
1210 		printf("       -i <prefix>   Prefix for include files. eg qb/ (nothing by default)\n");
1211 		printf("       -C <company>  Company name in copyright (default: Red Hat)\n");
1212 		printf("       -D <date>     Date to print at top of man pages (format not checked, default: today)\n");
1213 		printf("       -S <year>     Start year to print at end of copyright line (default: 2010)\n");
1214 		printf("       -Y <year>     Year to print at end of copyright line (default: today's year)\n");
1215 		printf("       -o <dir>      Write all man pages to <dir> (default: .)\n");
1216 		printf("       -d <dir>      Directory for XML files (default: ./xml/)\n");
1217 		printf("       -h            Print this usage text\n");
1218 	}
1219 	
1220 	static long get_year(char *optionarg, char optionchar)
1221 	{
1222 		long year = strtol(optionarg, NULL, 10);
1223 		/*
1224 		 * Don't make too many assumptions about the year. I was on call at the
1225 		 * 2000 rollover. #experience
1226 		 */
1227 		if (year == LONG_MIN || year == LONG_MAX ||
1228 		    year < 1900) {
1229 			fprintf(stderr, "Value passed to -%c is not a valid year number\n", optionchar);
1230 			return 0;
1231 		}
1232 		return year;
1233 	}
1234 	
1235 	int main(int argc, char *argv[])
1236 	{
1237 		xmlNode *rootdoc;
1238 		xmlDocPtr doc;
1239 		int opt;
1240 		char xml_filename[PATH_MAX];
1241 	
1242 		while ( (opt = getopt_long(argc, argv, "H:amqgcPD:Y:s:S:d:o:p:f:I:i:C:O:h?", NULL, NULL)) != EOF)
1243 		{
1244 			switch(opt)
1245 			{
1246 				case 'a':
1247 					print_ascii = 1;
1248 					print_man = 0;
1249 					break;
1250 				case 'm':
1251 					print_man = 1;
1252 					print_ascii = 0;
1253 					break;
1254 				case 'P':
1255 					print_params = 1;
1256 					break;
1257 				case 'g':
1258 					print_general = 1;
1259 					break;
1260 				case 'q':
1261 					quiet = 1;
1262 					break;
1263 				case 'c':
1264 					use_header_copyright = 1;
1265 					break;
1266 				case 'I':
1267 					headerfile = optarg;
1268 					break;
1269 				case 'i':
1270 					header_prefix = optarg;
1271 					break;
1272 				case 'C':
1273 					company = optarg;
1274 					break;
1275 				case 's':
1276 					man_section = optarg;
1277 					break;
1278 				case 'S':
1279 					start_year = get_year(optarg, 'S');
1280 					if (start_year == 0) {
1281 						return 1;
1282 					}
1283 					break;
1284 				case 'd':
1285 					xml_dir = optarg;
1286 					break;
1287 				case 'D':
1288 					manpage_date = optarg;
1289 					break;
1290 				case 'Y':
1291 					manpage_year = get_year(optarg, 'Y');
1292 					if (manpage_year == 0) {
1293 						return 1;
1294 					}
1295 					break;
1296 				case 'p':
1297 					package_name = optarg;
1298 					break;
1299 				case 'H':
1300 					header = optarg;
1301 					break;
1302 				case 'o':
1303 					output_dir = optarg;
1304 					break;
1305 				case 'O':
1306 				        header_src_dir = optarg;
1307 					break;
1308 				case '?':
1309 				case 'h':
1310 					usage(argv[0]);
1311 					return 0;
1312 			}
1313 		}
1314 	
1315 		if (argv[optind]) {
1316 			xml_file = argv[optind];
1317 		}
1318 		if (!xml_file) {
1319 			usage(argv[0]);
1320 			exit(1);
1321 		}
1322 	
1323 		if (!quiet) {
1324 			printf("reading %s ... ", xml_file);
1325 		}
1326 	
1327 		snprintf(xml_filename, sizeof(xml_filename), "%s/%s", xml_dir, xml_file);
1328 		doc = xmlParseFile(xml_filename);
1329 		if (doc == NULL) {
1330 			fprintf(stderr, "Error: unable to read xml file %s\n", xml_filename);
1331 			exit(1);
1332 		}
1333 	
1334 		rootdoc = xmlDocGetRootElement(doc);
1335 		if (!rootdoc) {
1336 			fprintf(stderr, "Can't find \"document root\"\n");
1337 			exit(1);
1338 		}
1339 		if (!quiet)
1340 			printf("done.\n");
1341 	
1342 		/* Get our header file name */
1343 		if (!headerfile) {
1344 			traverse_node(rootdoc, "compounddef", read_headername, &headerfile);
1345 	
1346 			if (use_header_copyright) {
1347 				/* And get the copyright line from this file if we can */
1348 				char file_path[PATH_MAX];
1349 				char file_line[256];
1350 				FILE *hfile;
1351 				int lineno = 0;
1352 	
1353 				snprintf(file_path, sizeof(file_path), "%s/%s", header_src_dir, headerfile);
1354 				hfile = fopen(file_path, "r");
1355 				if (hfile) {
1356 					/* Don't look too far, this should be at the top */
1357 					while (!feof(hfile) && (lineno++ < 10)) {
1358 						if (fgets(file_line, sizeof(file_line)-1, hfile)) {
1359 							if (strncmp(file_line, " * Copyright", 12) == 0) {
1360 								/* Keep the NL at the end of the buffer, it save us printing one */
1361 								strncpy(header_copyright, file_line+3, sizeof(header_copyright)-1);
1362 								break;
1363 							}
1364 						}
1365 					}
1366 					fclose(hfile);
1367 				}
1368 			}
1369 		}
1370 	
1371 		/* Default to *something* if it all goes wrong */
1372 		if (!headerfile) {
1373 			headerfile = "unknown.h";
1374 		}
1375 	
1376 		qb_list_init(&params_list);
1377 		qb_list_init(&retval_list);
1378 		structures_map = qb_hashtable_create(10);
1379 		function_map = qb_hashtable_create(10);
1380 		used_structures_map = qb_hashtable_create(10);
1381 	
1382 		/* Collect functions */
1383 		traverse_node(rootdoc, "memberdef", collect_functions, NULL);
1384 	
1385 		/* Collect enums */
1386 		traverse_node(rootdoc, "memberdef", collect_enums, NULL);
1387 	
1388 		/* print pages */
1389 		traverse_node(rootdoc, "memberdef", traverse_members, NULL);
1390 	
1391 		if (print_general) {
1392 			/* Generate and print a page for the headerfile itself */
1393 			traverse_node(rootdoc, "compounddef", traverse_members, (char *)headerfile);
1394 		}
1395 		return 0;
1396 	}
1397