search.c revision 3231ecdc
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 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 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, omask; 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 omask = umask(077); 184 fd = mkstemp(tmp); 185 umask(omask); 186 if (fd < 0) { 187 PopupWarning(man_globals, "Cant create temp file"); 188 return NULL; 189 } 190 mantmp = tmp; 191 192 manpath = getenv("MANPATH"); 193 if (manpath == NULL || streq(manpath, "")) { 194#ifdef MANCONF 195 if (!ReadManConfig(path)) 196#endif 197 { 198 strcpy(path, SYSMANPATH); 199#ifdef LOCALMANPATH 200 strcat(path, ":"); 201 strcat(path, LOCALMANPATH); 202#endif 203 } 204 } 205 else { 206 strcpy(path, manpath); 207 } 208 209 snprintf(label, sizeof(label), 210 "Results of apropos search on: %s", search_string); 211 212#ifdef NO_MANPATH_SUPPORT /* not quite correct, but the best I can do. */ 213 snprintf(cmdbuf, sizeof(cmdbuf), APROPOS_FORMAT, search_string, mantmp); 214#else 215 snprintf(cmdbuf, sizeof(cmdbuf), APROPOS_FORMAT, path, search_string, 216 mantmp); 217#endif 218 219 if (system(cmdbuf) != 0) { /* execute search. */ 220 snprintf(error_buf, sizeof(error_buf), 221 "Something went wrong trying to run %s\n", cmdbuf); 222 PopupWarning(man_globals, error_buf); 223 } 224 225 if ((file = fdopen(fd, "r")) == NULL) 226 PrintError("lost temp file? out of temp space?"); 227 228/* 229 * Since we keep the FD open we can remove the file safely, this 230 * will keep extra files out of /tmp. 231 */ 232 233 remove(mantmp); 234 235 snprintf(string_buf, sizeof(string_buf), "%s: nothing appropriate", 236 search_string); 237 238 /* 239 * Check first LOOKLINES lines for "nothing appropriate". 240 */ 241 242 count = 0; 243 flag = FALSE; 244 while ((fgets(cmp_str, BUFSIZ, file) != NULL) && (count < LOOKLINES)) { 245 size_t len = strlen(cmp_str); 246 247 if (len > 0 && cmp_str[len - 1] == '\n') /* strip off the '\n' */ 248 cmp_str[len - 1] = '\0'; 249 250 if (streq(cmp_str, string_buf)) { 251 flag = TRUE; 252 break; 253 } 254 count++; 255 } 256 257 /* 258 * If the file is less than this number of lines then assume that there is 259 * nothing appropriate found. This does not confuse the apropos filter. 260 */ 261 262 if (flag) { 263 fclose(file); 264 file = NULL; 265 ChangeLabel(man_globals->label, string_buf); 266 return (NULL); 267 } 268 269 snprintf(man_globals->manpage_title, sizeof(man_globals->manpage_title), 270 "%s", label); 271 ChangeLabel(man_globals->label, label); 272 fseek(file, 0L, SEEK_SET); /* reset file to point at top. */ 273 } 274 else { /* MANUAL SEARCH */ 275 file = DoManualSearch(man_globals, search_string); 276 if (file == NULL) { 277 snprintf(string_buf, sizeof(string_buf), "No manual entry for %s.", 278 search_string); 279 ChangeLabel(man_globals->label, string_buf); 280 if (man_globals->label == NULL) 281 PopupWarning(man_globals, string_buf); 282 return (NULL); 283 } 284 } 285 286 if (resources.clear_search_string) { 287 Arg arglist[1]; 288 Widget dialog; 289 290 dialog = XtNameToWidget(man_globals->search_widget, DIALOG); 291 if (dialog == NULL) 292 PopupWarning(man_globals, "Could not clear the search string."); 293 294 XtSetArg(arglist[0], XtNvalue, ""); 295 XtSetValues(dialog, arglist, (Cardinal) 1); 296 } 297 298 return (file); 299} 300 301/* Function Name: DoManualSearch 302 * Description: performs a manual search. 303 * Arguments: man_globals - the manual page specific globals. 304 * Returns: the filename of the man page. 305 */ 306 307#define NO_ENTRY -100 308 309static FILE * 310DoManualSearch(ManpageGlobals * man_globals, char *string) 311{ 312 int e_num = NO_ENTRY; 313 int i; 314 315/* search current section first. */ 316 317 i = man_globals->current_directory; 318 e_num = BEntrySearch(string, manual[i].entries, manual[i].nentries); 319 320/* search other sections. */ 321 322 if (e_num == NO_ENTRY) { 323 i = 0; /* At the exit of the loop i needs to 324 be the one we used. */ 325 while (TRUE) { 326 if (i == man_globals->current_directory) 327 if (++i >= sections) 328 return (NULL); 329 e_num = BEntrySearch(string, manual[i].entries, manual[i].nentries); 330 if (e_num != NO_ENTRY) 331 break; 332 if (++i >= sections) 333 return (NULL); 334 } 335 336/* 337 * Manual page found in some other section, unhighlight the current one. 338 */ 339 if (man_globals->manpagewidgets.box != NULL) 340 XawListUnhighlight(man_globals->manpagewidgets. 341 box[man_globals->current_directory]); 342 } 343 else { 344 /* 345 * Highlight the element we are searching for if it is in the directory 346 * listing currently being shown. 347 */ 348 if (man_globals->manpagewidgets.box != NULL) 349 XawListHighlight(man_globals->manpagewidgets.box[i], e_num); 350 } 351 return (FindManualFile(man_globals, i, e_num)); 352} 353 354/* Function Name: BEntrySearch 355 * Description: binary search through entries. 356 * Arguments: string - the string to match. 357 * first - the first entry in the list. 358 * number - the number of entries. 359 * Returns: a pointer to the entry found. 360 */ 361 362static int 363BEntrySearch(char *string, char **first, int number) 364{ 365 int check, cmp, len_cmp, global_number; 366 char *head, *tail; 367 368 global_number = 0; 369 while (TRUE) { 370 371 if (number == 0) { 372 return (NO_ENTRY); /* didn't find it. */ 373 } 374 375 check = number / 2; 376 377 head = strrchr(first[global_number + check], '/'); 378 if (head == NULL) 379 PrintError("index failure in BEntrySearch"); 380 head++; 381 382 tail = strrchr(head, '.'); 383 if (tail == NULL) 384 /* not an error, some systems (e.g. sgi) have only a .z suffix */ 385 tail = head + strlen(head); 386 387 cmp = strncmp(string, head, (tail - head)); 388 len_cmp = strlen(string) - (int) (tail - head); 389 390 if (cmp == 0 && len_cmp == 0) { 391 return (global_number + check); 392 } 393 else if (cmp < 0 || ((cmp == 0) && (len_cmp < 0))) 394 number = check; 395 else { /* cmp > 0 || ((cmp == 0) && (len_cmp > 0)) */ 396 397 global_number += (check + 1); 398 number -= (check + 1); 399 } 400 } 401} 402