/* File: helpsubs.c * Author: Fred Wobus (fw@sanger.ac.uk) * Copyright (C) J Thierry-Mieg and R Durbin, 1998 *------------------------------------------------------------------- * This file is part of the ACEDB genome database package, written by * Richard Durbin (MRC LMB, UK) rd@sanger.ac.uk, and * Jean Thierry-Mieg (CRBM du CNRS, France) mieg@kaa.cnrs-mop.fr * * SCCS: %W% %G% * Description: controls the help system, provides HTML parsing * Exported functions: * HISTORY: * Last edited: Dec 4 14:30 1998 (fw) * * Oct 12 12:27 1998 (fw): checkSubject now case-insensitive * * Oct 8 17:23 1998 (fw): removed warning, in case that an open-list tag (e.g. ). The warning tried to enforce that every type of list only has a certain type of items. * * Oct 8 11:36 1998 (fw): helpSubjectGetFilename takes over logic from readHelpfile to locate the file containing the help for a particular subject * Created: Tue Aug 18 16:11:07 1998 (fw) *------------------------------------------------------------------- */ #include "help_.h" /************************************************************/ static char *makeHtmlIndex (STORE_HANDLE handle); static char *makeHtmlImagePage (char *link, STORE_HANDLE handle); static HtmlNode *parseHtmlText (char *text, STORE_HANDLE handle); static BOOL parseSection (char **cp, HtmlNode **resultnode, STORE_HANDLE handle); /************************************************************/ /************ directory where help files are stored *********/ static char helpDir[MAXPATHLEN] = "" ; /************************************************************/ /* function to register the helpOnRoutine This can be called at any stage (before the first helpOn, or later on, it will affect the system next time helpOn is called. */ /************************************************************/ static QueryRoutine helpOnRoutine = 0; UTIL_FUNC_DEF QueryRoutine helpOnRegister (QueryRoutine func) /* call with func = 0x0 just to check whether anything has been registered yet */ { QueryRoutine old = helpOnRoutine ; if (func) helpOnRoutine = func ; return old ; } /************************************************************/ /* Sets the helpDir; */ /************************************************************/ UTIL_FUNC_DEF char *helpSetDir (char *dirname) { if (dirname) { strcpy (helpDir, dirname); if (filName (dirname,0,"rd")) return (char*)&helpDir[0]; else return (char*)0; } else { strcpy (helpDir, filGetFullPath ("whelp")); if (filName (helpDir, 0, "rd")) return (char*)&helpDir[0]; } return (char*)0; } /* helpGetDir */ /************************************************************/ /* return the current helpDirectory or initialise if not previously set */ UTIL_FUNC_DEF char *helpGetDir (void) { if (!*helpDir) return (helpSetDir(0)) ; return (char*)&helpDir[0]; } /* helpGetDir */ /************************************************************/ /* pop up help on the given subject, depending on the registered display function, that will be textual, in the built-in simple HTML browser or even launch an external browser to display the help document */ /************************************************************/ UTIL_FUNC_DEF BOOL helpOn (char *subject) { char *helpFilename; if (!helpGetDir() || !filName(helpGetDir(), "", "rd")) { messout ("Sorry, No help available ! " "Could not open the HTML help directory " "%s\n" "(%s)", helpGetDir(), messSysErrorText()); return FALSE; } helpFilename = helpSubjectGetFilename(subject); /* may be NULL if file could not be found, the registered helpOnRoutine has to cope with this case and may decide to display an index instead */ if (helpOnRoutine) return ((*helpOnRoutine)(helpFilename)); return (helpPrint (helpFilename)); /* textual help as default */ } /* helpOn */ /************************************************************/ UTIL_FUNC_DEF char *helpSubjectGetFilename (char *subject) /* this function attempts to find the file name corresponding to a particular help-subject. It will attempt to find a matching file according to the current settings of helpDir and HELP_FILE_EXTENSION. the subject '?' will just return ? again. This is a special code within the help system to tell the help display function that the user required some kind of help. Usually the helpOnRegister'd function would display a dynamically created index of the help-directory. this function can be even cleverer by doing keyword searches on and <H1> strings in files that might be relevant of no obvious match is found. */ { static char filename_array[MAXPATHLEN] = ""; char *filename = &filename_array[0]; char *subject_copy; Array dirList; if (subject == NULL) return NULL; if (strlen(subject) == 0) return NULL; if (strcmp(subject, "?") == 0) { /* return ? to signal that the calling function needs to display a dynamically created index or show some kind of help. */ /* if the construct page = htmlPageCreate(helpGetFilename(subject_requested)); is used, the resulting page will therefor be a marked up directory listing of helpsubjects */ strcpy (filename, "?"); return filename; } subject_copy = strnew (subject, 0); strcpy (filename, ""); /* intialise, if this is non-empty at the end of the loop, we found a matching helpfile */ while (TRUE) { /* simple attempt to locate file - path/helpDir/subject.html */ sprintf(filename, "%s%s%s.%s", filGetFullPath(helpGetDir()), SUBDIR_DELIMITER_STR, subject_copy, HELP_FILE_EXTENSION); if (filName(filename, 0, "r")) break; /* advanced attempt, try to find a matching file from the list of available ones by scanning the directory contents of the helpdirectory */ if ((dirList = filDirectoryCreate (helpGetDir(), HELP_FILE_EXTENSION, "r")) ) { int i; int matches; char *s; /* first look for an exact case-insensitive match */ strcpy (filename, ""); for (i = 0 ; i < arrayMax(dirList) ; i++) { s = arr(dirList,i,char*); if (strcasecmp (s, subject_copy) == 0) { sprintf(filename, "%s%s%s.%s", filGetFullPath(helpGetDir()), SUBDIR_DELIMITER_STR, s, HELP_FILE_EXTENSION); if (filName(filename, 0, "r")) break; /* exit for-loop */ strcpy (filename, ""); } } if (strlen(filename) > 0) break; /* exit while(true) loop */ /* count the number of filenames starting with the given subject string */ matches = 0; for (i = 0 ; i < arrayMax(dirList) ; i++) { s = arr(dirList,i,char*); if (strncasecmp (s, subject_copy, strlen(subject_copy)) == 0) { sprintf(filename, "%s%s%s.%s", filGetFullPath(helpGetDir()), SUBDIR_DELIMITER_STR, s, HELP_FILE_EXTENSION); ++matches; } } if (matches == 0) { strcpy (filename, ""); /* not found */ } else if (matches == 1) { /* the one exact match (already in filename string) is the complete filename */ if (filName(filename, 0, "r")) break; /* exit while(true) loop */ } else if (matches > 1) { /* construct a filename that we know won't work. But it may be used by the help display function to give a meaningful message to say that this subject is ambiguos. The returned filename is then considered a template, similar to 'ls subject*' so the help-display function may give a list of possible matching subjects. */ sprintf(filename, "%s%s%s", filGetFullPath(helpGetDir()), SUBDIR_DELIMITER_STR, subject_copy); break; } filDirectoryDestroy (dirList); } /* endif dirList */ /* file didn't exist, whichever way we tried so far, so we try to chop off the last bit of the subject name. In case trySubject was "Tree_Clone_Inside", we now go through the look again with "Tree_Clone" and re-try. */ if (strchr (subject_copy, '_')) { int j; j = strlen (subject_copy); while (subject_copy[j--] != '_') ; /* find the last _ char */ subject_copy[j + 1] = '\0'; } else { /* If we run out of trailing components, then we exit * anyway. */ strcpy (filename, ""); break; /* exit while(true)loop */ } } /* end-while(true) */ messfree (subject_copy); if (strcmp(filename, "") != 0) return filename; /* success */ if ((strcasecmp(subject, "index") == 0) || (strcasecmp(subject, "home") == 0) || (strcasecmp(subject, "toc") == 0)) { /* we asked for some kind of index-page but couldn't find it, so we can always try to return the question mark '?' which will ask the calling function to display a dynamically created index of help-subjects. */ strcpy (filename, "?"); return filename; } return NULL; /* failure - no file found */ } /* helpSubjectGetFilename */ /************************************************************/ /* helpPackage utility to find out the filename of a given link reference. Absolute filenames are returned unchanged, but relative filenames are expanded to be the full path of the helpfile. Can be used for html/gif files referred to by the HREF of anchor tags or the SRC or IMG tags */ /* NOTE: the pointer returned is a static copy, which is re-used everytime it is called. If the calling function wants to mess about with the returned string, a copy has to be made. NULL is returned if the resulting file can't be opened. the calling function can inspect the result of messSysErrorText(), the report the resaon for failure */ /************************************************************/ UTIL_FUNC_DEF char *helpLinkGetFilename (char *link) { static char link_path_array[MAXPATHLEN] = ""; char *link_path = &link_path_array[0]; if (link[0] == SUBDIR_DELIMITER) /* absolute path (UNIX) */ { strcpy (link_path, link); } else /* relative path */ { strcpy (link_path, helpGetDir()); strcat (link_path, SUBDIR_DELIMITER_STR); strcat (link_path, link); } if (filName(link_path, "", "r")) return link_path; return NULL; } /* helpLinkGetFilename */ /************************************************************/ /****************** ***********************/ /************** private helpPackage functions ***************/ /****************** ***********************/ /************************************************************/ HtmlPage *htmlPageCreate (char *helpFilename) /* complemeted by htmlPageDestroy */ { FILE *fil; HtmlPage *page = 0; if (!helpFilename) /* we could get a NULL filename */ return 0; /* here, which might come from helpSubjectGetFilename() that couldn't find a file matching the subject */ /* create a page with a marked up directory listing */ if (strcmp(helpFilename, "?") == 0) { page = messalloc (sizeof(HtmlPage)); page->handle = handleCreate(); page->htmlText = makeHtmlIndex(page->handle); if (!(page->root = parseHtmlText(page->htmlText, page->handle))) htmlPageDestroy(page); return page; } if (!(filName(helpFilename, "", "r"))) return 0; /* prevent error caused by unsucsessful filopen */ /* create a page inlining the image */ if (strcasecmp (helpFilename + (strlen(helpFilename)-4), ".gif") == 0) { page = messalloc (sizeof(HtmlPage)); page->handle = handleCreate(); page->htmlText = makeHtmlImagePage(helpFilename, page->handle); if (!(page->root = parseHtmlText(page->htmlText, page->handle))) htmlPageDestroy(page); return page; } /* assume HTML page */ if ((fil = filopen(helpFilename, "", "r"))) { page = htmlPageCreateFromFile (fil); filclose (fil); } return page; } /* htmlPageCreate */ /************************************************************/ HtmlPage *htmlPageCreateFromFile (FILE *fil) { HtmlPage *page; int fileSize; if (!fil) return (HtmlPage*)0; /* determine filesize */ rewind (fil); fseek (fil, 0, SEEK_END); fileSize = ftell (fil); rewind (fil); if (fileSize == 0) return (HtmlPage*)0; /* if we have a positive fileSize, we are pretty much guaranteed, that we'll get some HTML text and a parsetree */ page = messalloc (sizeof(HtmlPage)); page->handle = handleCreate(); /* grab the contents of the file */ page->htmlText = halloc ((fileSize + 1) * sizeof(char), page->handle); fread (page->htmlText, sizeof (char), fileSize, fil); page->htmlText[fileSize] = '\0'; /* add string terminator */ /* get parsetree */ page->root = parseHtmlText(page->htmlText, page->handle); return page; } /* htmlPageCreateFromFile */ /************************************************************/ void htmlPageDestroy (HtmlPage *page) { if (!page) return; /* clear all memory used during parsing of the page */ handleDestroy (page->handle); /* clear the memory taken up by the structure itself */ messfree (page); return; } /* htmlPageDestroy */ /************************************************************/ void stripSpaces (char *cp) /* utility to get rid of multiple spaces from a string */ /* we use it on node->text, where the text isn't within <PRE> tags */ { char *s ; int i ; /* strip unwanted white spaces from the text */ for (i = 0; i < strlen(cp); ++i) if (isspace ((int)cp[i])) cp[i] = ' ' ; while ((s = strstr (cp, " "))) { s[1] = 0 ; strcat (cp, s+2) ; } if (cp[strlen(cp)-1] == ' ') cp[strlen(cp)-1] = '\0' ; return ; } /* stripSpaces */ /************************************************************/ /****************** ***********************/ /****************** static functions ***********************/ /****************** ***********************/ /************************************************************/ /************************************************************/ /* as the helpviewer supports inlined images, it is easy to display image, even when they're not inlined as in <A HREF=image.gif>click here for image</A>. We just return a container page, that inlines the image */ /************************************************************/ static char *makeHtmlImagePage (char *link, STORE_HANDLE handle) { char *text; int len; len = 0; len = 7+6+strlen(filGetFilename(link))+8+10+strlen(link)+2; text = halloc((len+1)*sizeof(char), handle); sprintf (text, "<TITLE>Image %s" "", filGetFilename(link), link); text[len] = 0; return text; } /* makeHtmlImagePage */ /************************************************************/ /* reads the directory of helpDir and constructs an HTML-page containing a