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