search.c revision 3231ecdc
11abf7346Smrg/*
21abf7346Smrg
31abf7346SmrgCopyright (c) 1987, 1988  X Consortium
41abf7346Smrg
51abf7346SmrgPermission is hereby granted, free of charge, to any person obtaining
61abf7346Smrga copy of this software and associated documentation files (the
71abf7346Smrg"Software"), to deal in the Software without restriction, including
81abf7346Smrgwithout limitation the rights to use, copy, modify, merge, publish,
91abf7346Smrgdistribute, sublicense, and/or sell copies of the Software, and to
101abf7346Smrgpermit persons to whom the Software is furnished to do so, subject to
111abf7346Smrgthe following conditions:
121abf7346Smrg
131abf7346SmrgThe above copyright notice and this permission notice shall be included
141abf7346Smrgin all copies or substantial portions of the Software.
151abf7346Smrg
161abf7346SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
171abf7346SmrgOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
181abf7346SmrgMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
191abf7346SmrgIN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
201abf7346SmrgOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
211abf7346SmrgARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
221abf7346SmrgOTHER DEALINGS IN THE SOFTWARE.
231abf7346Smrg
241abf7346SmrgExcept as contained in this notice, the name of the X Consortium shall
251abf7346Smrgnot be used in advertising or otherwise to promote the sale, use or
261abf7346Smrgother dealings in this Software without prior written authorization
271abf7346Smrgfrom the X Consortium.
281abf7346Smrg
291abf7346Smrg*/
301abf7346Smrg
311abf7346Smrg
321abf7346Smrg#include "globals.h"
331abf7346Smrg#include "vendor.h"
341abf7346Smrg
356d36ef34Smrg/* Map <CR> and control-M to goto beginning of file. */
361abf7346Smrg
371abf7346Smrg#define SEARCHARGS 10
381abf7346Smrg
396d36ef34Smrgstatic FILE *DoManualSearch(ManpageGlobals *man_globals, char *string);
406d36ef34Smrgstatic int BEntrySearch(char *string, char **first, int number);
411abf7346Smrg
421abf7346Smrg/*	Function Name: MakeSearchWidget
431abf7346Smrg *	Description: This Function Creates the Search Widget.
446d36ef34Smrg *	Arguments: man_globals - the pseudo globals for this manpage.
451abf7346Smrg *                 w - the widgets parent
461abf7346Smrg *	Returns: the search widget.
471abf7346Smrg */
481abf7346Smrg
491abf7346Smrgvoid
501abf7346SmrgMakeSearchWidget(ManpageGlobals * man_globals, Widget parent)
511abf7346Smrg{
526d36ef34Smrg    Widget dialog, command, text, cancel;
536d36ef34Smrg    Arg arglist[2];
546d36ef34Smrg    Cardinal num_args = 0;
556d36ef34Smrg
566d36ef34Smrg    XtSetArg(arglist[0], XtNtransientFor, parent);
576d36ef34Smrg    man_globals->search_widget = XtCreatePopupShell(SEARCHNAME,
586d36ef34Smrg                                                    transientShellWidgetClass,
596d36ef34Smrg                                                    parent, arglist, 1);
606d36ef34Smrg
616d36ef34Smrg    if (resources.clear_search_string) {
626d36ef34Smrg        XtSetArg(arglist[0], XtNvalue, "");
636d36ef34Smrg        num_args++;
646d36ef34Smrg    }
651abf7346Smrg
666d36ef34Smrg    dialog = XtCreateManagedWidget(DIALOG, dialogWidgetClass,
676d36ef34Smrg                                   man_globals->search_widget,
686d36ef34Smrg                                   arglist, num_args);
691abf7346Smrg
706d36ef34Smrg    if ((text = XtNameToWidget(dialog, "value")) == (Widget) NULL)
716d36ef34Smrg        PopupWarning(NULL, "Could not find text widget in MakeSearchWidget.");
726d36ef34Smrg    else
736d36ef34Smrg        XtSetKeyboardFocus(dialog, text);
741abf7346Smrg
756d36ef34Smrg    XawDialogAddButton(dialog, MANUALSEARCH, NULL, NULL);
766d36ef34Smrg    XawDialogAddButton(dialog, APROPOSSEARCH, NULL, NULL);
776d36ef34Smrg    XawDialogAddButton(dialog, CANCEL, NULL, NULL);
781abf7346Smrg
791abf7346Smrg/*
806d36ef34Smrg * This is a bit gross, but it get the cancel button underneath the
811abf7346Smrg * others, and forms them up to the right size..
821abf7346Smrg */
831abf7346Smrg
846d36ef34Smrg    if (((command = XtNameToWidget(dialog, MANUALSEARCH)) == (Widget) NULL) ||
856d36ef34Smrg        ((cancel = XtNameToWidget(dialog, CANCEL)) == (Widget) NULL))
866d36ef34Smrg        PopupWarning(NULL,
876d36ef34Smrg                     "Could not find manual search widget in MakeSearchWidget.");
886d36ef34Smrg    else {
896d36ef34Smrg        static const char *half_size[] = {
906d36ef34Smrg            MANUALSEARCH, APROPOSSEARCH, NULL
916d36ef34Smrg        };
926d36ef34Smrg        static const char *full_size[] = {
936d36ef34Smrg            "label", "value", CANCEL, NULL
946d36ef34Smrg        };
956d36ef34Smrg
966d36ef34Smrg        num_args = 0;
976d36ef34Smrg        XtSetArg(arglist[num_args], XtNfromVert, command);
986d36ef34Smrg        num_args++;
996d36ef34Smrg        XtSetArg(arglist[num_args], XtNfromHoriz, NULL);
1006d36ef34Smrg        num_args++;
1016d36ef34Smrg        XtSetValues(cancel, arglist, num_args);
1026d36ef34Smrg        FormUpWidgets(dialog, full_size, half_size);
1036d36ef34Smrg    }
1041abf7346Smrg
1051abf7346Smrg}
1061abf7346Smrg
1071abf7346Smrg/*      Function Name: SearchString
1081abf7346Smrg *      Description: Returns the search string.
1091abf7346Smrg *      Arguments: man_globals - the globals.
1101abf7346Smrg *      Returns: the search string.
1111abf7346Smrg */
1121abf7346Smrg
1131abf7346Smrgstatic char *
1146d36ef34SmrgSearchString(ManpageGlobals * man_globals)
1151abf7346Smrg{
1166d36ef34Smrg    Widget dialog;
1171abf7346Smrg
1186d36ef34Smrg    dialog = XtNameToWidget(man_globals->search_widget, DIALOG);
1196d36ef34Smrg    if (dialog != NULL)
1206d36ef34Smrg        return (XawDialogGetValueString(dialog));
1211abf7346Smrg
1226d36ef34Smrg    PopupWarning(man_globals,
123da4a0041Smrg                 "Could not get the search string, no search will be performed.");
1246d36ef34Smrg    return (NULL);
1251abf7346Smrg}
1261abf7346Smrg
1276d36ef34Smrg
1281abf7346Smrg/*	Function Name: DoSearch
1291abf7346Smrg *	Description: This function performs a search for a man page or apropos
1301abf7346Smrg *                   search upon search string.
1316d36ef34Smrg *	Arguments: man_globals - the pseudo globals for this manpage.
1321abf7346Smrg *                 type - the type of search.
1331abf7346Smrg *	Returns: none.
1341abf7346Smrg */
1351abf7346Smrg
1361abf7346Smrg#define LOOKLINES 6
1371abf7346Smrg
1386d36ef34Smrg/*
1391abf7346Smrg * Manual searches look through the list of manual pages for the right one
1401abf7346Smrg * with a binary search.
1411abf7346Smrg *
1421abf7346Smrg * Apropos searches still exec man -k.
1431abf7346Smrg *
1441abf7346Smrg * If nothing is found then I send a warning message to the user, and do
1451abf7346Smrg * nothing.
1461abf7346Smrg */
1471abf7346Smrg
1481abf7346SmrgFILE *
1491abf7346SmrgDoSearch(ManpageGlobals * man_globals, int type)
1501abf7346Smrg{
1516d36ef34Smrg    char cmdbuf[BUFSIZ], *mantmp, *manpath;
1526d36ef34Smrg    char tmp[BUFSIZ], path[BUFSIZ];
1536d36ef34Smrg    char string_buf[BUFSIZ], cmp_str[BUFSIZ], error_buf[BUFSIZ];
1546d36ef34Smrg    char *search_string = SearchString(man_globals);
1556d36ef34Smrg    FILE *file;
1563231ecdcSchristos    int fd, omask;
1576d36ef34Smrg    int count;
1586d36ef34Smrg    Boolean flag;
1596d36ef34Smrg
1606d36ef34Smrg    if (search_string == NULL)
1616d36ef34Smrg        return (NULL);
1626d36ef34Smrg
1636d36ef34Smrg    /* If the string is empty or starts with a space then do not search */
1646d36ef34Smrg
1656d36ef34Smrg    if (streq(search_string, "")) {
1666d36ef34Smrg        PopupWarning(man_globals, "Search string is empty.");
1676d36ef34Smrg        return (NULL);
1686d36ef34Smrg    }
1696d36ef34Smrg
1706d36ef34Smrg    if (strlen(search_string) >= BUFSIZ) {
1716d36ef34Smrg        PopupWarning(man_globals, "Search string too long.");
1726d36ef34Smrg        return (NULL);
1736d36ef34Smrg    }
1746d36ef34Smrg    if (search_string[0] == ' ') {
1756d36ef34Smrg        PopupWarning(man_globals, "First character cannot be a space.");
1766d36ef34Smrg        return (NULL);
1771abf7346Smrg    }
1781abf7346Smrg
1796d36ef34Smrg    if (type == APROPOS) {
1806d36ef34Smrg        char label[BUFSIZ];
1816d36ef34Smrg
1826d36ef34Smrg        strcpy(tmp, MANTEMP);   /* get a temp file. */
1833231ecdcSchristos	omask = umask(077);
1846d36ef34Smrg        fd = mkstemp(tmp);
1853231ecdcSchristos	umask(omask);
1866d36ef34Smrg        if (fd < 0) {
1876d36ef34Smrg            PopupWarning(man_globals, "Cant create temp file");
1886d36ef34Smrg            return NULL;
1896d36ef34Smrg        }
1906d36ef34Smrg        mantmp = tmp;
1916d36ef34Smrg
1926d36ef34Smrg        manpath = getenv("MANPATH");
1936d36ef34Smrg        if (manpath == NULL || streq(manpath, "")) {
1941abf7346Smrg#ifdef MANCONF
1956d36ef34Smrg            if (!ReadManConfig(path))
1961abf7346Smrg#endif
1976d36ef34Smrg            {
1986d36ef34Smrg                strcpy(path, SYSMANPATH);
1991abf7346Smrg#ifdef LOCALMANPATH
2006d36ef34Smrg                strcat(path, ":");
2016d36ef34Smrg                strcat(path, LOCALMANPATH);
2021abf7346Smrg#endif
2036d36ef34Smrg            }
2046d36ef34Smrg        }
2056d36ef34Smrg        else {
2066d36ef34Smrg            strcpy(path, manpath);
2076d36ef34Smrg        }
2081abf7346Smrg
2096d36ef34Smrg        snprintf(label, sizeof(label),
2106d36ef34Smrg                 "Results of apropos search on: %s", search_string);
2111abf7346Smrg
2126d36ef34Smrg#ifdef NO_MANPATH_SUPPORT       /* not quite correct, but the best I can do. */
2136d36ef34Smrg        snprintf(cmdbuf, sizeof(cmdbuf), APROPOS_FORMAT, search_string, mantmp);
2141abf7346Smrg#else
2156d36ef34Smrg        snprintf(cmdbuf, sizeof(cmdbuf), APROPOS_FORMAT, path, search_string,
2166d36ef34Smrg                 mantmp);
2171abf7346Smrg#endif
2181abf7346Smrg
2196d36ef34Smrg        if (system(cmdbuf) != 0) {      /* execute search. */
2206d36ef34Smrg            snprintf(error_buf, sizeof(error_buf),
2216d36ef34Smrg                     "Something went wrong trying to run %s\n", cmdbuf);
2226d36ef34Smrg            PopupWarning(man_globals, error_buf);
2236d36ef34Smrg        }
2241abf7346Smrg
2256d36ef34Smrg        if ((file = fdopen(fd, "r")) == NULL)
2266d36ef34Smrg            PrintError("lost temp file? out of temp space?");
2271abf7346Smrg
2286d36ef34Smrg/*
2296d36ef34Smrg * Since we keep the FD open we can remove the file safely, this
2306d36ef34Smrg * will keep extra files out of /tmp.
2311abf7346Smrg */
2321abf7346Smrg
2336d36ef34Smrg        remove(mantmp);
2346d36ef34Smrg
2356d36ef34Smrg        snprintf(string_buf, sizeof(string_buf), "%s: nothing appropriate",
2366d36ef34Smrg                 search_string);
2376d36ef34Smrg
2386d36ef34Smrg        /*
2396d36ef34Smrg         * Check first LOOKLINES lines for "nothing appropriate".
2406d36ef34Smrg         */
2416d36ef34Smrg
2426d36ef34Smrg        count = 0;
2436d36ef34Smrg        flag = FALSE;
2446d36ef34Smrg        while ((fgets(cmp_str, BUFSIZ, file) != NULL) && (count < LOOKLINES)) {
245da4a0041Smrg            size_t len = strlen(cmp_str);
246da4a0041Smrg
247da4a0041Smrg            if (len > 0 && cmp_str[len - 1] == '\n')  /* strip off the '\n' */
248da4a0041Smrg                cmp_str[len - 1] = '\0';
2496d36ef34Smrg
2506d36ef34Smrg            if (streq(cmp_str, string_buf)) {
2516d36ef34Smrg                flag = TRUE;
2526d36ef34Smrg                break;
2536d36ef34Smrg            }
2546d36ef34Smrg            count++;
2556d36ef34Smrg        }
2566d36ef34Smrg
2576d36ef34Smrg        /*
2586d36ef34Smrg         * If the file is less than this number of lines then assume that there is
259da4a0041Smrg         * nothing appropriate found. This does not confuse the apropos filter.
2606d36ef34Smrg         */
2616d36ef34Smrg
2626d36ef34Smrg        if (flag) {
2636d36ef34Smrg            fclose(file);
2646d36ef34Smrg            file = NULL;
2656d36ef34Smrg            ChangeLabel(man_globals->label, string_buf);
2666d36ef34Smrg            return (NULL);
2676d36ef34Smrg        }
2686d36ef34Smrg
2696d36ef34Smrg        snprintf(man_globals->manpage_title, sizeof(man_globals->manpage_title),
2706d36ef34Smrg                 "%s", label);
2716d36ef34Smrg        ChangeLabel(man_globals->label, label);
2726d36ef34Smrg        fseek(file, 0L, SEEK_SET);      /* reset file to point at top. */
2731abf7346Smrg    }
2746d36ef34Smrg    else {                      /* MANUAL SEARCH */
2756d36ef34Smrg        file = DoManualSearch(man_globals, search_string);
2766d36ef34Smrg        if (file == NULL) {
2776d36ef34Smrg            snprintf(string_buf, sizeof(string_buf), "No manual entry for %s.",
2786d36ef34Smrg                     search_string);
2796d36ef34Smrg            ChangeLabel(man_globals->label, string_buf);
2806d36ef34Smrg            if (man_globals->label == NULL)
2816d36ef34Smrg                PopupWarning(man_globals, string_buf);
2826d36ef34Smrg            return (NULL);
2836d36ef34Smrg        }
2841abf7346Smrg    }
2851abf7346Smrg
2866d36ef34Smrg    if (resources.clear_search_string) {
2876d36ef34Smrg        Arg arglist[1];
2886d36ef34Smrg        Widget dialog;
2891abf7346Smrg
2906d36ef34Smrg        dialog = XtNameToWidget(man_globals->search_widget, DIALOG);
2916d36ef34Smrg        if (dialog == NULL)
2926d36ef34Smrg            PopupWarning(man_globals, "Could not clear the search string.");
2931abf7346Smrg
2946d36ef34Smrg        XtSetArg(arglist[0], XtNvalue, "");
2956d36ef34Smrg        XtSetValues(dialog, arglist, (Cardinal) 1);
2966d36ef34Smrg    }
2971abf7346Smrg
2986d36ef34Smrg    return (file);
2991abf7346Smrg}
3001abf7346Smrg
3011abf7346Smrg/*	Function Name: DoManualSearch
3021abf7346Smrg *	Description: performs a manual search.
3031abf7346Smrg *	Arguments: man_globals - the manual page specific globals.
3041abf7346Smrg *	Returns: the filename of the man page.
3051abf7346Smrg */
3061abf7346Smrg
3071abf7346Smrg#define NO_ENTRY -100
3081abf7346Smrg
3096d36ef34Smrgstatic FILE *
3106d36ef34SmrgDoManualSearch(ManpageGlobals * man_globals, char *string)
3111abf7346Smrg{
3126d36ef34Smrg    int e_num = NO_ENTRY;
3136d36ef34Smrg    int i;
3141abf7346Smrg
3151abf7346Smrg/* search current section first. */
3166d36ef34Smrg
3176d36ef34Smrg    i = man_globals->current_directory;
3186d36ef34Smrg    e_num = BEntrySearch(string, manual[i].entries, manual[i].nentries);
3191abf7346Smrg
3201abf7346Smrg/* search other sections. */
3211abf7346Smrg
3226d36ef34Smrg    if (e_num == NO_ENTRY) {
3236d36ef34Smrg        i = 0;                  /* At the exit of the loop i needs to
3246d36ef34Smrg                                   be the one we used. */
3256d36ef34Smrg        while (TRUE) {
3266d36ef34Smrg            if (i == man_globals->current_directory)
3276d36ef34Smrg                if (++i >= sections)
3286d36ef34Smrg                    return (NULL);
3296d36ef34Smrg            e_num = BEntrySearch(string, manual[i].entries, manual[i].nentries);
3306d36ef34Smrg            if (e_num != NO_ENTRY)
3316d36ef34Smrg                break;
3326d36ef34Smrg            if (++i >= sections)
3336d36ef34Smrg                return (NULL);
3346d36ef34Smrg        }
3351abf7346Smrg
3361abf7346Smrg/*
3371abf7346Smrg * Manual page found in some other section, unhighlight the current one.
3381abf7346Smrg */
3396d36ef34Smrg        if (man_globals->manpagewidgets.box != NULL)
3406d36ef34Smrg            XawListUnhighlight(man_globals->manpagewidgets.
3416d36ef34Smrg                               box[man_globals->current_directory]);
3426d36ef34Smrg    }
3436d36ef34Smrg    else {
3446d36ef34Smrg        /*
3456d36ef34Smrg         * Highlight the element we are searching for if it is in the directory
3466d36ef34Smrg         * listing currently being shown.
3476d36ef34Smrg         */
3486d36ef34Smrg        if (man_globals->manpagewidgets.box != NULL)
3496d36ef34Smrg            XawListHighlight(man_globals->manpagewidgets.box[i], e_num);
3506d36ef34Smrg    }
3516d36ef34Smrg    return (FindManualFile(man_globals, i, e_num));
3521abf7346Smrg}
3531abf7346Smrg
3541abf7346Smrg/*	Function Name: BEntrySearch
3551abf7346Smrg *	Description: binary search through entries.
3561abf7346Smrg *	Arguments: string - the string to match.
3571abf7346Smrg *                 first - the first entry in the list.
3581abf7346Smrg *                 number - the number of entries.
3591abf7346Smrg *	Returns: a pointer to the entry found.
3601abf7346Smrg */
3611abf7346Smrg
3621abf7346Smrgstatic int
3636d36ef34SmrgBEntrySearch(char *string, char **first, int number)
3641abf7346Smrg{
3656d36ef34Smrg    int check, cmp, len_cmp, global_number;
3666d36ef34Smrg    char *head, *tail;
3671abf7346Smrg
3686d36ef34Smrg    global_number = 0;
3696d36ef34Smrg    while (TRUE) {
3701abf7346Smrg
3716d36ef34Smrg        if (number == 0) {
3726d36ef34Smrg            return (NO_ENTRY);  /* didn't find it. */
3736d36ef34Smrg        }
3741abf7346Smrg
3756d36ef34Smrg        check = number / 2;
3761abf7346Smrg
3776d36ef34Smrg        head = strrchr(first[global_number + check], '/');
3786d36ef34Smrg        if (head == NULL)
3796d36ef34Smrg            PrintError("index failure in BEntrySearch");
3806d36ef34Smrg        head++;
3811abf7346Smrg
3826d36ef34Smrg        tail = strrchr(head, '.');
3836d36ef34Smrg        if (tail == NULL)
3846d36ef34Smrg            /* not an error, some systems (e.g. sgi) have only a .z suffix */
3856d36ef34Smrg            tail = head + strlen(head);
3866d36ef34Smrg
3876d36ef34Smrg        cmp = strncmp(string, head, (tail - head));
3886d36ef34Smrg        len_cmp = strlen(string) - (int) (tail - head);
3896d36ef34Smrg
3906d36ef34Smrg        if (cmp == 0 && len_cmp == 0) {
3916d36ef34Smrg            return (global_number + check);
3926d36ef34Smrg        }
3936d36ef34Smrg        else if (cmp < 0 || ((cmp == 0) && (len_cmp < 0)))
3946d36ef34Smrg            number = check;
3956d36ef34Smrg        else {                  /* cmp > 0 || ((cmp == 0) && (len_cmp > 0)) */
3966d36ef34Smrg
3976d36ef34Smrg            global_number += (check + 1);
3986d36ef34Smrg            number -= (check + 1);
3996d36ef34Smrg        }
4001abf7346Smrg    }
4011abf7346Smrg}
402