search.c revision 6d36ef34
1/*
2
3Copyright (c) 1987, 1988  X Consortium
4
5Permission is hereby granted, free of charge, to any person obtaining
6a copy of this software and associated documentation files (the
7"Software"), to deal in the Software without restriction, including
8without limitation the rights to use, copy, modify, merge, publish,
9distribute, sublicense, and/or sell copies of the Software, and to
10permit persons to whom the Software is furnished to do so, subject to
11the following conditions:
12
13The above copyright notice and this permission notice shall be included
14in all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
20OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22OTHER DEALINGS IN THE SOFTWARE.
23
24Except as contained in this notice, the name of the X Consortium shall
25not be used in advertising or otherwise to promote the sale, use or
26other dealings in this Software without prior written authorization
27from 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
39static FILE *DoManualSearch(ManpageGlobals *man_globals, char *string);
40static 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
49void
50MakeSearchWidget(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
113static char *
114SearchString(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 preformed.");
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
148FILE *
149DoSearch(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            if (cmp_str[strlen(cmp_str) - 1] == '\n')   /* strip off the '\n' */
244                cmp_str[strlen(cmp_str) - 1] = '\0';
245
246            if (streq(cmp_str, string_buf)) {
247                flag = TRUE;
248                break;
249            }
250            count++;
251        }
252
253        /*
254         * If the file is less than this number of lines then assume that there is
255         * nothing apropriate found. This does not confuse the apropos filter.
256         */
257
258        if (flag) {
259            fclose(file);
260            file = NULL;
261            ChangeLabel(man_globals->label, string_buf);
262            return (NULL);
263        }
264
265        snprintf(man_globals->manpage_title, sizeof(man_globals->manpage_title),
266                 "%s", label);
267        ChangeLabel(man_globals->label, label);
268        fseek(file, 0L, SEEK_SET);      /* reset file to point at top. */
269    }
270    else {                      /* MANUAL SEARCH */
271        file = DoManualSearch(man_globals, search_string);
272        if (file == NULL) {
273            snprintf(string_buf, sizeof(string_buf), "No manual entry for %s.",
274                     search_string);
275            ChangeLabel(man_globals->label, string_buf);
276            if (man_globals->label == NULL)
277                PopupWarning(man_globals, string_buf);
278            return (NULL);
279        }
280    }
281
282    if (resources.clear_search_string) {
283        Arg arglist[1];
284        Widget dialog;
285
286        dialog = XtNameToWidget(man_globals->search_widget, DIALOG);
287        if (dialog == NULL)
288            PopupWarning(man_globals, "Could not clear the search string.");
289
290        XtSetArg(arglist[0], XtNvalue, "");
291        XtSetValues(dialog, arglist, (Cardinal) 1);
292    }
293
294    return (file);
295}
296
297/*	Function Name: DoManualSearch
298 *	Description: performs a manual search.
299 *	Arguments: man_globals - the manual page specific globals.
300 *	Returns: the filename of the man page.
301 */
302
303#define NO_ENTRY -100
304
305static FILE *
306DoManualSearch(ManpageGlobals * man_globals, char *string)
307{
308    int e_num = NO_ENTRY;
309    int i;
310
311/* search current section first. */
312
313    i = man_globals->current_directory;
314    e_num = BEntrySearch(string, manual[i].entries, manual[i].nentries);
315
316/* search other sections. */
317
318    if (e_num == NO_ENTRY) {
319        i = 0;                  /* At the exit of the loop i needs to
320                                   be the one we used. */
321        while (TRUE) {
322            if (i == man_globals->current_directory)
323                if (++i >= sections)
324                    return (NULL);
325            e_num = BEntrySearch(string, manual[i].entries, manual[i].nentries);
326            if (e_num != NO_ENTRY)
327                break;
328            if (++i >= sections)
329                return (NULL);
330        }
331
332/*
333 * Manual page found in some other section, unhighlight the current one.
334 */
335        if (man_globals->manpagewidgets.box != NULL)
336            XawListUnhighlight(man_globals->manpagewidgets.
337                               box[man_globals->current_directory]);
338    }
339    else {
340        /*
341         * Highlight the element we are searching for if it is in the directory
342         * listing currently being shown.
343         */
344        if (man_globals->manpagewidgets.box != NULL)
345            XawListHighlight(man_globals->manpagewidgets.box[i], e_num);
346    }
347    return (FindManualFile(man_globals, i, e_num));
348}
349
350/*	Function Name: BEntrySearch
351 *	Description: binary search through entries.
352 *	Arguments: string - the string to match.
353 *                 first - the first entry in the list.
354 *                 number - the number of entries.
355 *	Returns: a pointer to the entry found.
356 */
357
358static int
359BEntrySearch(char *string, char **first, int number)
360{
361    int check, cmp, len_cmp, global_number;
362    char *head, *tail;
363
364    global_number = 0;
365    while (TRUE) {
366
367        if (number == 0) {
368            return (NO_ENTRY);  /* didn't find it. */
369        }
370
371        check = number / 2;
372
373        head = strrchr(first[global_number + check], '/');
374        if (head == NULL)
375            PrintError("index failure in BEntrySearch");
376        head++;
377
378        tail = strrchr(head, '.');
379        if (tail == NULL)
380            /* not an error, some systems (e.g. sgi) have only a .z suffix */
381            tail = head + strlen(head);
382
383        cmp = strncmp(string, head, (tail - head));
384        len_cmp = strlen(string) - (int) (tail - head);
385
386        if (cmp == 0 && len_cmp == 0) {
387            return (global_number + check);
388        }
389        else if (cmp < 0 || ((cmp == 0) && (len_cmp < 0)))
390            number = check;
391        else {                  /* cmp > 0 || ((cmp == 0) && (len_cmp > 0)) */
392
393            global_number += (check + 1);
394            number -= (check + 1);
395        }
396    }
397}
398