1 /*
2
3 Copyright (c) 1987, 1988 X Consortium
4
5 Permission is hereby granted, free of charge, to any person obtaining
6 a copy of this software and associated documentation files (the
7 "Software"), to deal in the Software without restriction, including
8 without limitation the rights to use, copy, modify, merge, publish,
9 distribute, sublicense, and/or sell copies of the Software, and to
10 permit persons to whom the Software is furnished to do so, subject to
11 the following conditions:
12
13 The above copyright notice and this permission notice shall be included
14 in all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 OTHER DEALINGS IN THE SOFTWARE.
23
24 Except as contained in this notice, the name of the X Consortium shall
25 not be used in advertising or otherwise to promote the sale, use or
26 other dealings in this Software without prior written authorization
27 from the X Consortium.
28
29 */
30
31
32 #include "globals.h"
33 #include "vendor.h"
34
35 /* Map <CR> and control-M to goto beginning of file. */
36
37 #define SEARCHARGS 10
38
39 static FILE *DoManualSearch(ManpageGlobals *man_globals, char *string);
40 static int BEntrySearch(char *string, char **first, int number);
41
42 /* Function Name: MakeSearchWidget
43 * Description: This Function Creates the Search Widget.
44 * Arguments: man_globals - the pseudo globals for this manpage.
45 * w - the widgets parent
46 * Returns: the search widget.
47 */
48
49 void
MakeSearchWidget(ManpageGlobals * man_globals,Widget parent)50 MakeSearchWidget(ManpageGlobals * man_globals, Widget parent)
51 {
52 Widget dialog, command, text, cancel;
53 Arg arglist[2];
54 Cardinal num_args = 0;
55
56 XtSetArg(arglist[0], XtNtransientFor, parent);
57 man_globals->search_widget = XtCreatePopupShell(SEARCHNAME,
58 transientShellWidgetClass,
59 parent, arglist, 1);
60
61 if (resources.clear_search_string) {
62 XtSetArg(arglist[0], XtNvalue, "");
63 num_args++;
64 }
65
66 dialog = XtCreateManagedWidget(DIALOG, dialogWidgetClass,
67 man_globals->search_widget,
68 arglist, num_args);
69
70 if ((text = XtNameToWidget(dialog, "value")) == (Widget) NULL)
71 PopupWarning(NULL, "Could not find text widget in MakeSearchWidget.");
72 else
73 XtSetKeyboardFocus(dialog, text);
74
75 XawDialogAddButton(dialog, MANUALSEARCH, NULL, NULL);
76 XawDialogAddButton(dialog, APROPOSSEARCH, NULL, NULL);
77 XawDialogAddButton(dialog, CANCEL, NULL, NULL);
78
79 /*
80 * This is a bit gross, but it get the cancel button underneath the
81 * others, and forms them up to the right size..
82 */
83
84 if (((command = XtNameToWidget(dialog, MANUALSEARCH)) == (Widget) NULL) ||
85 ((cancel = XtNameToWidget(dialog, CANCEL)) == (Widget) NULL))
86 PopupWarning(NULL,
87 "Could not find manual search widget in MakeSearchWidget.");
88 else {
89 static const char *half_size[] = {
90 MANUALSEARCH, APROPOSSEARCH, NULL
91 };
92 static const char *full_size[] = {
93 "label", "value", CANCEL, NULL
94 };
95
96 num_args = 0;
97 XtSetArg(arglist[num_args], XtNfromVert, command);
98 num_args++;
99 XtSetArg(arglist[num_args], XtNfromHoriz, NULL);
100 num_args++;
101 XtSetValues(cancel, arglist, num_args);
102 FormUpWidgets(dialog, full_size, half_size);
103 }
104
105 }
106
107 /* Function Name: SearchString
108 * Description: Returns the search string.
109 * Arguments: man_globals - the globals.
110 * Returns: the search string.
111 */
112
113 static char *
SearchString(ManpageGlobals * man_globals)114 SearchString(ManpageGlobals * man_globals)
115 {
116 Widget dialog;
117
118 dialog = XtNameToWidget(man_globals->search_widget, DIALOG);
119 if (dialog != NULL)
120 return (XawDialogGetValueString(dialog));
121
122 PopupWarning(man_globals,
123 "Could not get the search string, no search will be performed.");
124 return (NULL);
125 }
126
127
128 /* Function Name: DoSearch
129 * Description: This function performs a search for a man page or apropos
130 * search upon search string.
131 * Arguments: man_globals - the pseudo globals for this manpage.
132 * type - the type of search.
133 * Returns: none.
134 */
135
136 #define LOOKLINES 6
137
138 /*
139 * Manual searches look through the list of manual pages for the right one
140 * with a binary search.
141 *
142 * Apropos searches still exec man -k.
143 *
144 * If nothing is found then I send a warning message to the user, and do
145 * nothing.
146 */
147
148 FILE *
DoSearch(ManpageGlobals * man_globals,int type)149 DoSearch(ManpageGlobals * man_globals, int type)
150 {
151 char cmdbuf[BUFSIZ], *mantmp, *manpath;
152 char tmp[BUFSIZ], path[BUFSIZ];
153 char string_buf[BUFSIZ], cmp_str[BUFSIZ], error_buf[BUFSIZ];
154 char *search_string = SearchString(man_globals);
155 FILE *file;
156 int fd;
157 int count;
158 Boolean flag;
159
160 if (search_string == NULL)
161 return (NULL);
162
163 /* If the string is empty or starts with a space then do not search */
164
165 if (streq(search_string, "")) {
166 PopupWarning(man_globals, "Search string is empty.");
167 return (NULL);
168 }
169
170 if (strlen(search_string) >= BUFSIZ) {
171 PopupWarning(man_globals, "Search string too long.");
172 return (NULL);
173 }
174 if (search_string[0] == ' ') {
175 PopupWarning(man_globals, "First character cannot be a space.");
176 return (NULL);
177 }
178
179 if (type == APROPOS) {
180 char label[BUFSIZ];
181
182 strcpy(tmp, MANTEMP); /* get a temp file. */
183 fd = mkstemp(tmp);
184 if (fd < 0) {
185 PopupWarning(man_globals, "Cant create temp file");
186 return NULL;
187 }
188 mantmp = tmp;
189
190 manpath = getenv("MANPATH");
191 if (manpath == NULL || streq(manpath, "")) {
192 #ifdef MANCONF
193 if (!ReadManConfig(path))
194 #endif
195 {
196 strcpy(path, SYSMANPATH);
197 #ifdef LOCALMANPATH
198 strcat(path, ":");
199 strcat(path, LOCALMANPATH);
200 #endif
201 }
202 }
203 else {
204 strcpy(path, manpath);
205 }
206
207 snprintf(label, sizeof(label),
208 "Results of apropos search on: %s", search_string);
209
210 #ifdef NO_MANPATH_SUPPORT /* not quite correct, but the best I can do. */
211 snprintf(cmdbuf, sizeof(cmdbuf), APROPOS_FORMAT, search_string, mantmp);
212 #else
213 snprintf(cmdbuf, sizeof(cmdbuf), APROPOS_FORMAT, path, search_string,
214 mantmp);
215 #endif
216
217 if (system(cmdbuf) != 0) { /* execute search. */
218 snprintf(error_buf, sizeof(error_buf),
219 "Something went wrong trying to run %s\n", cmdbuf);
220 PopupWarning(man_globals, error_buf);
221 }
222
223 if ((file = fdopen(fd, "r")) == NULL)
224 PrintError("lost temp file? out of temp space?");
225
226 /*
227 * Since we keep the FD open we can remove the file safely, this
228 * will keep extra files out of /tmp.
229 */
230
231 remove(mantmp);
232
233 snprintf(string_buf, sizeof(string_buf), "%s: nothing appropriate",
234 search_string);
235
236 /*
237 * Check first LOOKLINES lines for "nothing appropriate".
238 */
239
240 count = 0;
241 flag = FALSE;
242 while ((fgets(cmp_str, BUFSIZ, file) != NULL) && (count < LOOKLINES)) {
243 size_t len = strlen(cmp_str);
244
245 if (len > 0 && cmp_str[len - 1] == '\n') /* strip off the '\n' */
246 cmp_str[len - 1] = '\0';
247
248 if (streq(cmp_str, string_buf)) {
249 flag = TRUE;
250 break;
251 }
252 count++;
253 }
254
255 /*
256 * If the file is less than this number of lines then assume that there is
257 * nothing appropriate found. This does not confuse the apropos filter.
258 */
259
260 if (flag) {
261 fclose(file);
262 file = NULL;
263 ChangeLabel(man_globals->label, string_buf);
264 return (NULL);
265 }
266
267 snprintf(man_globals->manpage_title, sizeof(man_globals->manpage_title),
268 "%s", label);
269 ChangeLabel(man_globals->label, label);
270 fseek(file, 0L, SEEK_SET); /* reset file to point at top. */
271 }
272 else { /* MANUAL SEARCH */
273 file = DoManualSearch(man_globals, search_string);
274 if (file == NULL) {
275 snprintf(string_buf, sizeof(string_buf), "No manual entry for %s.",
276 search_string);
277 ChangeLabel(man_globals->label, string_buf);
278 if (man_globals->label == NULL)
279 PopupWarning(man_globals, string_buf);
280 return (NULL);
281 }
282 }
283
284 if (resources.clear_search_string) {
285 Arg arglist[1];
286 Widget dialog;
287
288 dialog = XtNameToWidget(man_globals->search_widget, DIALOG);
289 if (dialog == NULL)
290 PopupWarning(man_globals, "Could not clear the search string.");
291
292 XtSetArg(arglist[0], XtNvalue, "");
293 XtSetValues(dialog, arglist, (Cardinal) 1);
294 }
295
296 return (file);
297 }
298
299 /* Function Name: DoManualSearch
300 * Description: performs a manual search.
301 * Arguments: man_globals - the manual page specific globals.
302 * Returns: the filename of the man page.
303 */
304
305 #define NO_ENTRY -100
306
307 static FILE *
DoManualSearch(ManpageGlobals * man_globals,char * string)308 DoManualSearch(ManpageGlobals * man_globals, char *string)
309 {
310 int e_num = NO_ENTRY;
311 int i;
312
313 /* search current section first. */
314
315 i = man_globals->current_directory;
316 e_num = BEntrySearch(string, manual[i].entries, manual[i].nentries);
317
318 /* search other sections. */
319
320 if (e_num == NO_ENTRY) {
321 i = 0; /* At the exit of the loop i needs to
322 be the one we used. */
323 while (TRUE) {
324 if (i == man_globals->current_directory)
325 if (++i >= sections)
326 return (NULL);
327 e_num = BEntrySearch(string, manual[i].entries, manual[i].nentries);
328 if (e_num != NO_ENTRY)
329 break;
330 if (++i >= sections)
331 return (NULL);
332 }
333
334 /*
335 * Manual page found in some other section, unhighlight the current one.
336 */
337 if (man_globals->manpagewidgets.box != NULL)
338 XawListUnhighlight(man_globals->manpagewidgets.
339 box[man_globals->current_directory]);
340 }
341 else {
342 /*
343 * Highlight the element we are searching for if it is in the directory
344 * listing currently being shown.
345 */
346 if (man_globals->manpagewidgets.box != NULL)
347 XawListHighlight(man_globals->manpagewidgets.box[i], e_num);
348 }
349 return (FindManualFile(man_globals, i, e_num));
350 }
351
352 /* Function Name: BEntrySearch
353 * Description: binary search through entries.
354 * Arguments: string - the string to match.
355 * first - the first entry in the list.
356 * number - the number of entries.
357 * Returns: a pointer to the entry found.
358 */
359
360 static int
BEntrySearch(char * string,char ** first,int number)361 BEntrySearch(char *string, char **first, int number)
362 {
363 int check, cmp, len_cmp, global_number;
364 char *head, *tail;
365
366 global_number = 0;
367 while (TRUE) {
368
369 if (number == 0) {
370 return (NO_ENTRY); /* didn't find it. */
371 }
372
373 check = number / 2;
374
375 head = strrchr(first[global_number + check], '/');
376 if (head == NULL)
377 PrintError("index failure in BEntrySearch");
378 head++;
379
380 tail = strrchr(head, '.');
381 if (tail == NULL)
382 /* not an error, some systems (e.g. sgi) have only a .z suffix */
383 tail = head + strlen(head);
384
385 cmp = strncmp(string, head, (tail - head));
386 len_cmp = strlen(string) - (int) (tail - head);
387
388 if (cmp == 0 && len_cmp == 0) {
389 return (global_number + check);
390 }
391 else if (cmp < 0 || ((cmp == 0) && (len_cmp < 0)))
392 number = check;
393 else { /* cmp > 0 || ((cmp == 0) && (len_cmp > 0)) */
394
395 global_number += (check + 1);
396 number -= (check + 1);
397 }
398 }
399 }
400