1 /* $Id: output_apropos.c,v 1.12 2003/04/17 03:03:41 jtalkington Exp $ */
2 
3 #include "common.h"
4 #include "output_apropos.h"
5 
6 #ifndef DISABLE_APROPOS
7 /* the tree of all of the apropos_node s
8  */
9 static struct section_node *apropos_tree = NULL;
10 
11 /* output_apropos()
12  * outputs the results of apropos sorted by section and with references back
13  * to the program's URL
14  */
15 void
output_apropos(char * keyword)16 output_apropos(char *keyword) {
17 	FILE *ap_fh = NULL;
18 	char line[BUFSIZ];
19 	char *command = NULL;
20 
21 	if((Config.apropos != NULL) && ((Config.disabled & D_APROPOS) == 0)) {
22 		command = strdup(Config.apropos);
23 	} else {
24 		output_apropos_denied();
25 		return;
26 	}
27 
28 
29 	command = str_app_s(command, " ");
30 	command = str_app_s(command, keyword);
31 
32 	output_header("Keyword search", keyword);
33 	if((ap_fh = popen(command, "r")) == NULL) {
34 		printf("<h2>Error opening pipe to: %s</h2>\n", command);
35 		output_footer();
36 		return;
37 	} else {
38 		while(fgets(line, sizeof(line), ap_fh) != NULL) {
39 			apr_add_line(line);
40 		}
41 
42 		printf("<h2>Keyword search for %s results:</h2>\n<hr />\n", keyword);
43 		printf("<table width=\"100%%\">\n");
44 		output_section_node(&apropos_tree);
45 		printf("</table>\n");
46 		output_footer();
47 		pclose(ap_fh);
48 #ifdef M2W_CLEANUP
49 		cleanup_apropos_section(&apropos_tree);
50 		apropos_tree = NULL;
51 #endif /* M2W_CLEANUP */
52 	}
53 
54 	free(command);
55 
56 }
57 
58 /* break down the line and add links if we have are not in command mode */
59 void
apr_add_line(char * line)60 apr_add_line(char *line) {
61 	char *linked_line = NULL;
62 	char *program = NULL;
63 	char *section = NULL;
64 	char *prog_start = NULL;
65 	char *prog_end = NULL;
66 	char *sec_start = NULL;
67 	char *sec_end = NULL;
68 	char *white_start = NULL;
69 	char *script_name = get_query_value(SCRIPT_NAME);
70 	char *prog_extra = calloc(1,1);
71 
72 	if(line == NULL) {
73 		return;
74 	}
75 
76 	/* kill the newline */
77 	memset(line + strlen(line) - 1, '\0', 1);
78 
79 	prog_start = line;
80 	prog_end = prog_start;
81 
82 	/* get the program name */
83 	while(*prog_end != ' ' && *prog_end != '\t' && *prog_end != '(') {
84 		prog_end++;
85 	}
86 
87 	program = strndup(prog_start, prog_end - prog_start);
88 
89 	/* extract the section */
90 	sec_start = strchr(prog_end, '(');
91 
92 	if(sec_start != NULL) {
93 
94 		sec_end = strchr(sec_start, ')');
95 
96 		if(sec_end != NULL) {
97 
98 			section = strndup(sec_start + 1, sec_end - (sec_start + 1));
99 
100 			white_start = sec_start;
101 
102 			while(white_start > prog_end && (*(white_start - 1) == ' ' || *(white_start - 1) == '\t')) {
103 				white_start--;
104 			}
105 
106 			/* get any non whitespace extras after the program name but before
107 			 * the section start (for entries with [foo] after the program name
108 			 */
109 
110 			while(prog_end < white_start) {
111 				prog_extra = str_app_c(prog_extra, *prog_end);
112 				prog_end++;
113 			}
114 
115 			/* get to the start of the description (-) */
116 			while(*sec_end != '-') {
117 				sec_end++;
118 			}
119 
120 			if(script_name != NULL) {
121 				linked_line = calloc(1, strlen(APROPOS_HREF) + (2 * (strlen(program) + 1)) + (strlen(prog_extra) + 1) + (strlen(section) + 1) + (strlen(script_name) + 1) + strlen(line) + 1);
122 				sprintf(linked_line, APROPOS_HREF, script_name, program, section, program, prog_extra, section, sec_end);
123 
124 			} else {
125 				linked_line = strdup(line);
126 			}
127 		} else {
128 			linked_line = strdup(line);
129 		}
130 	} else {
131 		linked_line = strdup(line);
132 	}
133 
134 	apr_add_to_tree(&apropos_tree, program, section, linked_line);
135 
136 	if(program != NULL) {
137 		free(program);
138 		program = NULL;
139 	}
140 
141 	if(prog_extra != NULL) {
142 		free(prog_extra);
143 		prog_extra = NULL;
144 	}
145 
146 	if(section != NULL) {
147 		free(section);
148 		section = NULL;
149 	}
150 
151 	free(linked_line);
152 	linked_line = NULL;
153 }
154 
155 /* apr_add_to_tree()
156  * adds an apropos section node and program node to the tree
157  */
158 void
apr_add_to_tree(struct section_node ** sec_node,char * program,char * section,char * line)159 apr_add_to_tree(struct section_node **sec_node, char *program, char *section, char *line) {
160 	int result = 0;
161 
162 	if((*sec_node) == NULL) {
163 		(*sec_node) = calloc(1, sizeof(struct section_node));
164 
165 		(*sec_node)->left = NULL;
166 		(*sec_node)->right = NULL;
167 		(*sec_node)->data = NULL;
168 		if(section != NULL) {
169 			(*sec_node)->name = strdup(section);
170 		} else {
171 			(*sec_node)->name = strdup(program);
172 		};
173 		apr_add_prog(&(*sec_node)->data, program, line);
174 	} else {
175 		result = strcmp((*sec_node)->name, section);
176 
177 		if(result == 0) {
178 			apr_add_prog(&(*sec_node)->data, program, line);
179 		} else if(result < 0) {
180 			apr_add_to_tree(&(*sec_node)->right, program, section, line);
181 		} else {
182 			apr_add_to_tree(&(*sec_node)->left, program, section, line);
183 		}
184 	}
185 }
186 
187 /* apr_add_prog()
188  * adds a program to an apropos_node tree
189  */
190 void
apr_add_prog(struct apropos_node ** node,char * program,char * line)191 apr_add_prog(struct apropos_node **node, char *program, char *line) {
192 	int result = 0;
193 
194 	if((*node) == NULL) {
195 		(*node) = calloc(1, sizeof(struct apropos_node));
196 
197 		(*node)->left = NULL;
198 		(*node)->right = NULL;
199 		(*node)->name = strdup(program);
200 		(*node)->line = strdup(line);
201 	} else {
202 		result = strcmp((*node)->name, program);
203 
204 		if(result == 0) {
205 			/* it already exists, so just dump it */
206 			return;
207 		} else if(result < 0) {
208 			apr_add_prog(&(*node)->right, program, line);
209 		} else {
210 			apr_add_prog(&(*node)->left, program, line);
211 		}
212 	}
213 }
214 
215 /* output_section_node()
216  * outputs all of the elements of the apropos_tree recursively (and therefore
217  * sorted by section
218  */
219 void
output_section_node(struct section_node ** section)220 output_section_node(struct section_node **section) {
221 
222 	if((*section) == NULL) {
223 		return;
224 	}
225 
226 	if((*section)->left != NULL) {
227 		output_section_node(&(*section)->left);
228 	}
229 
230 	output_apropos_node(&(*section)->data);
231 
232 	if((*section)->right != NULL) {
233 		output_section_node(&(*section)->right);
234 	}
235 }
236 
237 /* output_apropos_node()
238  * outputs the program node of a section recursively
239  */
240 
241 void
output_apropos_node(struct apropos_node ** node)242 output_apropos_node(struct apropos_node **node) {
243 
244 	if((*node) == NULL) {
245 		return;
246 	}
247 
248 	if((*node)->left != NULL) {
249 		output_apropos_node(&(*node)->left);
250 	}
251 
252 	printf("%s\n", (*node)->line);
253 
254 	output_apropos_node(&(*node)->right);
255 }
256 
257 #ifdef M2W_CLEANUP
258 
259 /* cleanup_apropos_section()
260  * cleans up a section node of the apropos_tree
261  */
262 void
cleanup_apropos_section(struct section_node ** node)263 cleanup_apropos_section(struct section_node **node) {
264 	struct section_node *right = NULL;
265 
266 	if((*node) == NULL) {
267 		return;
268 	} else {
269 		right = (*node)->right;
270 	}
271 
272 	if((*node)->left != NULL) {
273 		cleanup_apropos_section(&(*node)->left);
274 		(*node)->left = NULL;
275 	}
276 
277 	cleanup_apropos_node(&(*node)->data);
278 	(*node)->data = NULL;
279 
280 	free((*node)->name);
281 	(*node)->name = NULL;
282 
283 	free(*node);
284 
285 	if(right != NULL) {
286 		cleanup_apropos_section(&right);
287 	}
288 }
289 
290 /* cleanup_apropos_node()
291  * cleans up the apropos_node part of a section node
292  */
293 void
cleanup_apropos_node(struct apropos_node ** node)294 cleanup_apropos_node(struct apropos_node **node) {
295 	struct apropos_node *right = NULL;
296 
297 	if((*node) == NULL) {
298 		return;
299 	} else {
300 		right = (*node)->right;
301 	}
302 
303 	if((*node)->left != NULL) {
304 		cleanup_apropos_node(&(*node)->left);
305 		(*node)->left = NULL;
306 	}
307 
308 	free((*node)->name);
309 	(*node)->name = NULL;
310 
311 	free((*node)->line);
312 	(*node)->line = NULL;
313 
314 	free(*node);
315 
316 	if(right != NULL) {
317 		cleanup_apropos_node(&right);
318 	}
319 }
320 #endif /* M2W_CLEANUP */
321 
322 #else /* DISABLE_APROPOS */
323 
324 /* wrapper for if apropos is disabled */
325 void
output_apropos(char * keyword)326 output_apropos(char *keyword) {
327 	output_apropos_denied();
328 }
329 
330 #endif /* DISABLE_APROPOS */
331 
332 /* prints out a page if apropos searching is disabled */
333 void
output_apropos_denied()334 output_apropos_denied() {
335 	output_header("Keyword Search Disabled", NULL);
336 	printf("<h2>Keyword (apropos) search disabled</h2>\n");
337 	output_footer();
338 }
339