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