From: joe_banta AT perceptics DOT com (Joseph Banta) Subject: Try out this MAN.C (man page viewer) via your web browser 12 Jun 1998 00:10:32 -0700 Message-ID: <01BD9527.B8C344F0.cygnus.gnu-win32@HERBY> Mime-Version: 1.0 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 8bit To: "'gnu-win32 AT cygnus DOT com'" Compile the code below, and with the command 'man ', you can view man pages with your web browser, 'less' or 'notepad'. (depending on how you set the MANHANDLER environment variable) Read the header below for more info, or hack into the code yourself. -----cut here ----- /* ** Title: man.c ** ** Author: Joseph Banta, Perceptics Corp., Knoxville, TN, June 1998 ** joe_banta AT perceptics DOT com ** ** Usage: man [-m|--manonly|-i|--infoonly] ** ** Prerequisites: ** cygwinb19.dll ** bash, tkinfo, wish, sed ** groff (if you want to view man pages via less or notepad; ** otherwise, set the environment variable MANHANDLER ** to the complete path of your web browser ** [i.e., 'c:\Progra~1\MSIE\iexplore.exe']) ** ** Purpose: ** This is a 'man' emulator written for win32, cygnus style. ** It was originally based on 'man2html' by dj AT delorie DOT com, ** but its functionality is extended a bit. What it does is ** search through the directories in the environment variables ** INFOPATH and MANPATH for info and man pages, respectively, ** and if an info page is found, 'tkinfo' is called, otherwise ** the application named in the environment variable MANHANDLER ** (by default, 'less') is called to view the man page. If the ** MANHANDLER application contains the string 'iexplore' or ** 'netscape', then this program converts the man page to html ** format for the browser. If the MANHANDLER application name ** contains the string 'more' or 'less', the man page is piped ** through groff to be viewed appropriately. Otherwise (i.e., ** if MANHANDLER is defined as 'notepad'), the page is piped ** through groff into plain text format. */ #include #include #include #include extern char* getenv(char*); extern char* strdup(char*); extern char* strchr(char*, char); int in_tt = 0; int next_line_dd = 0; int need_undl = 0; int got_regular_line = 0; int need_re = 0; int fill_mode = 1; int current_BI = 0; int skip_nl = 0; typedef struct StringList { char *string; struct StringList *next; } StringList, *StringList_ptr; char *get_token(char *, char *); void clean(char *); void un_bi(FILE *); int process_line_til_regular(FILE *, FILE *); void bol(FILE *); void eol(FILE *); void twoggle(char *, char *, char *, FILE *); int process_line(FILE *, FILE *); int nullString(char*); void htmlTarget(char *, char *, char *, char *); void formatedTextTarget(char *, char *, char *); void textTarget(char *, char *, char *, char *, char *); void checkForCalls(char*[]); void usage(char*); StringList_ptr initializedStringList_ptr(char *); int main( int argc, char **argv ) { DIR *dir, *subDir; StringList_ptr manRequest=NULL, thisRequest; struct dirent *entry, *subEntry; char line[1000], thisPath[1000], target[1000], *util, *util1, **manDir, **infoDir, *systemDrive, *baseDir, *manHandler, *manPath, *infoPath, *tempDir; int i, j, k, numPaths, numInfos, mode=0; if (argc == 1) usage(argv[0]); if(nullString(systemDrive = getenv("SYSTEMDRIVE"))) systemDrive = strdup("C:\\"); if(nullString(baseDir = getenv("PERCEPTICS"))) if(nullString(baseDir = getenv("CYGROOT"))) baseDir = strdup("/usr/local"); for(i=0; baseDir[i]!='\0'; i++) if(baseDir[i] == '\\') baseDir[i] = '/'; if(nullString(manPath = getenv("MANPATH"))) { sprintf(line, "%s/man", baseDir); manPath = strdup(line); } if(nullString(infoPath = getenv("INFOPATH"))) { sprintf(line, "%s/info", baseDir); infoPath = strdup(line); } if(nullString(tempDir = getenv("TEMPDIR"))) { sprintf(line, "%s/temp", systemDrive); tempDir = strdup(line); } if(nullString(manHandler = getenv("MANHANDLER"))) manHandler = strdup("less"); /* Separate out the components of manPath into manDir[] */ for(numPaths=1, util=manPath; util=strchr(util, ':'); numPaths++, util++){ if(util == NULL) break; } manDir = (char**) malloc(numPaths * sizeof(char*)); for(i=0, util=manPath; inext) = initializedStringList_ptr(argv[i]); } /* Iterate through each requested name... */ for(thisRequest=manRequest; thisRequest!=NULL; thisRequest=thisRequest->next){ target[0]='\0'; /* First, look for 'info' pages, unless the user has specified only ** ** to search 'man' pages with the option '-m'. */ if(mode != 1) { sprintf(thisPath, "%s.info", thisRequest->string); for(j=0; jd_name, ".") || !strcmp(entry->d_name, "..")) continue; if(!strcmp(entry->d_name, thisPath)) { sprintf(target, "%s", infoDir[j]); break; } } } /* If an info page was found, display the page, then go on to the ** ** next request. */ if(target[0] != '\0') { sprintf(line, "tkinfo -dir %s %s &", target, thisRequest->string); system(line); continue; } } /* Look for 'man' pages, unless the user has specified only to ** ** search 'info' pages with the option '-i'. */ if(mode != 2) { for(j=0; jd_name, ".") || !strcmp(entry->d_name, "..")) continue; sprintf(thisPath, "%s/%s", manDir[j], entry->d_name); if((subDir = opendir(thisPath)) == NULL) continue; while((subEntry = readdir(subDir)) != NULL) { if(!strcmp(subEntry->d_name, ".") || !strcmp(subEntry->d_name, "..")) continue; if(!strncmp(subEntry->d_name, thisRequest->string, (k=strlen(thisRequest->string))) && (subEntry->d_name[k] == '.')) { sprintf(target, "%s/%s", thisPath, subEntry->d_name); break; } } if(target[0] != '\0') break; } } } if(target[0] == '\0') { fprintf(stderr, "No manual entry found for '%s'\n", thisRequest->string); continue; } /* If the manHandler app name contains the string 'netscape' ** ** or 'iexplore', format the man page as HTML */ if(strstr(manHandler, "netscape") || strstr(manHandler, "iexplore")) htmlTarget(thisRequest->string, target, tempDir, manHandler); /* If the manHandler app name contains the string 'more' ** ** or 'less', try to format the man page with groff */ else if(strstr(manHandler, "more") || strstr(manHandler, "less")) formatedTextTarget(target, manHandler, baseDir); /* Otherwise, try to remove all the formatting with groff */ else textTarget(target, manHandler, baseDir, tempDir, thisRequest->string); } return 0; } char * get_token(char *inp, char *buf) { char *obuf = buf; int quoted = 0; /* skip whitespace */ while (*inp && isspace(*inp)) inp++; if (*inp == 0) return 0; while (*inp) { switch (*inp) { case '"': quoted = 1-quoted; break; case '\\': *buf++ = *inp; *buf++ = *++inp; break; default: if (isspace(*inp) && !quoted) { *buf = 0; return inp; } *buf++ = *inp; break; } inp++; } *buf = 0; return inp; } void clean(char *cp) { char foo[1000]; char *rp = foo; char *ocp = cp; if (strncmp(cp, ".if t ", 6) == 0) cp += 6; while (*cp) { switch (*cp) { case '\\': cp++; switch (*cp) { case 'E': case 'F': case 'g': case 'b': case 'r': case 'B': *rp++ = '\\'; *rp++ = *cp++; break; case '/': case '-': case '\\': case '+': case '.': case 10: case 0: case ' ': case '=': case '\'': case '`': case '[': case ']': case ':': case '}': case '{': *rp++ = *cp++; break; case '|': case '^': case '"': case 'd': case 'u': case 'n': case '&': case 'w': case '%': case 'v': case 'k': cp++; break; case 't': *rp++ = ' '; cp++; break; case '0': *rp++ = ' '; cp++; break; case 'c': if (cp[1] == '\n') { skip_nl = 1; cp++; } cp++; break; case 'e': *rp++ = '\\'; cp++; break; case 's': cp++; cp++; while (isdigit(*cp)) cp++; break; case 'f': if (current_BI) { *rp++ = '<'; *rp++ = '/'; *rp++ = current_BI; *rp++ = '>'; current_BI = 0; } if (in_tt) { strcpy(rp, ""); rp += 5; in_tt = 0; } switch (*++cp) { case '(': if (cp[1] == 'C' && cp[2] == 'W') { strcpy(rp, ""); rp += 4; in_tt = 1; cp += 2; } else fprintf(stderr, "unknown font %.3s\n", cp); break; case 'B': current_BI = 'b'; *rp++ = '<'; *rp++ = 'b'; *rp++ = '>'; break; case 'R': case 'P': break; case 'I': current_BI = 'i'; *rp++ = '<'; *rp++ = 'i'; *rp++ = '>'; break; } cp++; break; case '*': cp++; if (cp[0] == '(') { cp++; if (cp[0] == 'l' && cp[1] == 'q') *rp++ = '`'; else if (cp[0] == 'r' && cp[1] == 'q') *rp++ = '\''; else { sprintf(rp, "[%.2s]", cp); rp += 4; } cp += 2; } else if (cp[0] == 'r') { cp++; strcpy(rp, "RCS"); rp += 3; } else { sprintf(rp, "[%c]", *cp); rp += 3; } break; case '(': if (cp[1] == 'c' && cp[2] == 'o') *rp++ = 0xa9; else if (cp[1] == 'b' && cp[2] == 'v') *rp++ = '|'; else if (cp[1] == 'e' && cp[2] == 'm') *rp++ = ' '; else if (cp[1] == '+' && cp[2] == '-') *rp++ = 0xb1; else if (cp[1] == 't' && cp[2] == 'i') *rp++ = '~'; else if (cp[1] == 't' && cp[2] == 's') *rp++ = '"'; else if (cp[1] == 'p' && cp[2] == 'l') *rp++ = '+'; else if (cp[1] == 'm' && cp[2] == 'i') *rp++ = '-'; else if (cp[1] == 'f' && cp[2] == 'm') *rp++ = '\''; else if (cp[1] == 'm' && cp[2] == 'u') *rp++ = 'x'; else if (cp[1] == 'b' && cp[2] == 'u') { strcpy(rp, "
  • "); rp += 4; } else if (cp[1] == '>' && cp[2] == '=') { *rp++ = '>'; *rp++ = '='; } else if (cp[1] == '*' && cp[2] == '*') { *rp++ = '*'; *rp++ = '*'; } else fprintf(stderr, "unknown meta-character (%c%c\n", cp[1], cp[2]); cp += 3; break; default: fprintf(stderr, "unknown escape \\%c (%d)\n", *cp, *cp); break; } break; case '&': *rp++ = '&'; *rp++ = 'a'; *rp++ = 'm'; *rp++ = 'p'; *rp++ = ';'; cp++; break; case '<': *rp++ = '&'; *rp++ = 'l'; *rp++ = 't'; *rp++ = ';'; cp++; break; case '>': *rp++ = '&'; *rp++ = 'g'; *rp++ = 't'; *rp++ = ';'; cp++; break; default: *rp++ = *cp++; break; } } *rp = 0; strcpy(ocp, foo); } void un_bi(FILE *outFile) { if (current_BI) { fprintf(outFile, "", current_BI); current_BI = 0; } } int process_line_til_regular(FILE *inFile, FILE *outFile) { got_regular_line = 0; while (!got_regular_line) process_line(inFile, outFile); } void bol(FILE *outFile) { got_regular_line = 1; if (next_line_dd) fprintf(outFile, "
    "); next_line_dd = 0; } void eol(FILE *outFile) { if (!fill_mode) fprintf(outFile, "
    \n"); } void twoggle(char *a, char *b, char *l, FILE *outFile) { int first = 1; char *c; char buf[1000]; bol(outFile); while ((l = get_token(l, buf))) { clean(buf); c = first ? a : b; if (c) fprintf(outFile, "<%s>%s", c, buf, c); else fprintf(outFile, "%s", buf); if (a && b && strcmp(a, b) == 0) { fputc(' ', outFile); } first = 1-first; } un_bi(outFile); if (!skip_nl) fprintf(outFile, "\n"); eol(outFile); got_regular_line = 1; } int process_line(FILE *inFile, FILE *outFile) { char buf[1000], cmd[10], b1[1000]; char token[1000]; if (fgets(buf, 999, inFile) == 0) return 0; skip_nl = 0; if (buf[0] != '.') { if (strncmp(buf, "'\\\"", 3) == 0) return 1; clean(buf); bol(outFile); fprintf(outFile, "\n%s\n", buf); if (buf[0] == 0 || buf[0] == '\n') fprintf(outFile, "

    \n"); eol(outFile); return 1; } if (sscanf(buf, "%s %[^\n]", cmd, buf) == 1) buf[0] = 0; if (strcmp(cmd, "..") == 0) { } else if (strcmp(cmd, ".B") == 0) { if (buf[0]) { twoggle("b", "b", buf, outFile); } else { fprintf(outFile, "\n"); process_line_til_regular(inFile, outFile); fprintf(outFile, "\n"); } } else if (strcmp(cmd, ".I") == 0) { if (buf[0]) { twoggle("i", "i", buf, outFile); } else { fprintf(outFile, "\n"); process_line_til_regular(inFile, outFile); fprintf(outFile, "\n"); } } else if (strcmp(cmd, ".BI") == 0) { twoggle("b", "i", buf, outFile); } else if (strcmp(cmd, ".IB") == 0) { twoggle("i", "b", buf, outFile); } else if (strcmp(cmd, ".BR") == 0) { twoggle("b", 0, buf, outFile); } else if (strcmp(cmd, ".RB") == 0) { twoggle(0, "b", buf, outFile); } else if (strcmp(cmd, ".IR") == 0) { twoggle("i", 0, buf, outFile); } else if (strcmp(cmd, ".RI") == 0) { twoggle(0, "i", buf, outFile); } else if (strcmp(cmd, ".nf") == 0) { if (fill_mode) fprintf(outFile, "

    \n");
        fill_mode = 0;
      }
      else if (strcmp(cmd, ".fi") == 0)
      {
        if (!fill_mode)
          fprintf(outFile, "
    \n"); fill_mode = 1; } else if (strcmp(cmd, ".br") == 0 || strcmp(cmd, ".Sp") == 0 || strcmp(cmd, ".ti") == 0) { if (need_undl) { need_undl = 0; fprintf(outFile, "\n"); } fprintf(outFile, "
    \n"); } else if (strcmp(cmd, ".LP") == 0 || strcmp(cmd, ".PP") == 0 || strcmp(cmd, ".sp") == 0 || strcmp(cmd, ".P") == 0) { if (need_undl) { need_undl = 0; fprintf(outFile, "\n"); } fprintf(outFile, "\n

    \n"); } else if (strcmp(cmd, ".RS") == 0) { fprintf(outFile, "

      \n"); need_re ++; } else if (strcmp(cmd, ".RE") == 0) { if (need_re) { fprintf(outFile, "
    \n"); need_re --; } } else if (strcmp(cmd, ".SH") == 0 || strcmp(cmd, ".SS") == 0) { char *cp = buf; int got_token = 0; while (need_re) { fprintf(outFile, "\n"); need_re--; } if (need_undl) { fprintf(outFile, "\n"); need_undl = 0; } fprintf(outFile, "\n

    \n"); while (cp = get_token(cp, token)) { got_token = 1; clean(token); fprintf(outFile, "%s ", token); } if (!got_token) { if (fgets(buf, 999, inFile) == 0) return 0; fprintf(outFile, "%s\n", buf); } fprintf(outFile, "

      \n\n"); un_bi(outFile); got_regular_line = 1; if (!fill_mode) fprintf(outFile, "\n"); fill_mode = 1; } else if (strcmp(cmd, ".SM") == 0) { if (buf[0]) { bol(outFile); clean(buf); fprintf(outFile, "%s\n", buf); eol(outFile); } else { fprintf(outFile, "\n"); process_line_til_regular(inFile, outFile); fprintf(outFile, "\n"); } } else if (strcmp(cmd, ".TH") == 0) { int all_upper = 1, i; get_token(buf, buf); for (i=0; buf[i]; i++) if (islower(buf[i])) all_upper = 0; if (all_upper) for (i=0; buf[i]; i++) if (isupper(buf[i])) buf[i] = tolower(buf[i]); fprintf(outFile, "\n", buf); fprintf(outFile, "
        \n"); } else if (strcmp(cmd, ".TP") == 0 || strcmp(cmd, ".Tp") == 0) { if (!need_undl) { fprintf(outFile, "

        \n"); need_undl = 1; } fprintf(outFile, "
        \n"); next_line_dd = 0; process_line_til_regular(inFile, outFile); next_line_dd = 1; } else if (strcmp(cmd, ".IP") == 0) { if (!need_undl) { fprintf(outFile, "

        \n"); need_undl = 1; } get_token(buf, buf); clean(buf); fprintf(outFile, "
        %s\n", buf); next_line_dd = 1; } else if (strcmp(cmd, ".TQ") == 0) { fprintf(outFile, "
        \n"); next_line_dd = 0; process_line_til_regular(inFile, outFile); next_line_dd = 1; } else if (strcmp(cmd, ".FN") == 0) { bol(outFile); get_token(buf, buf); fprintf(outFile, "%s\n", buf); got_regular_line = 1; eol(outFile); } /* Tcl macros */ else if (strcmp(cmd, ".AP") == 0) { char *cp = buf; cp = get_token(cp, token); fprintf(outFile, "

        %s\n", token); cp = get_token(cp, token); fprintf(outFile, " %s\n", token); cp = get_token(cp, token); fprintf(outFile, " (%s) -\n", token); } else if (strcmp(cmd, ".DS") == 0) { fprintf(outFile, "

        \n");
          }
          else if (strcmp(cmd, ".DE") == 0)
          {
            fprintf(outFile, "
        \n"); } /* end of Tcl macros */ else if (strcmp(cmd, ".\"") == 0) { } else if (strcmp(cmd, ".de") == 0) { do { if (fgets(buf, 999, inFile) == 0) return 0; } while (buf[0] != '.' || buf[1] != '.'); } return 1; } int nullString(char *name) { if(name == NULL) return 1; else if(name[0] == '\0') return 1; return 0; } void htmlTarget(char *name, char *target, char *tempDir, char *manHandler) { FILE *inFile, *outFile; char htmlFile[1000], shellVersion[1000], command[1000]; int i, j, k; if((inFile = fopen(target, "rb")) == NULL) { fprintf(stderr, "Error: can't open '%s' for reading.\n", target); return; } sprintf(htmlFile, "%s\\%s.html", tempDir, name); if((outFile = fopen(htmlFile, "wb")) == NULL) { fprintf(stderr, "Error: can't write to temporary html file.\n"); return; } while (process_line(inFile, outFile)) { } fprintf(outFile, "
      \n\n"); fclose(outFile); fclose(inFile); /* find all the back-slashes in 'htmlFile' and replace each with 2 */ for(j=k=0; htmlFile[j]!='\0'; j++, k++) { if(htmlFile[j] == '\\') shellVersion[k++] = '\\'; shellVersion[k] = htmlFile[j]; } shellVersion[k] = '\0'; sprintf(command, "%s %s; rm %s &", manHandler, shellVersion, shellVersion); system(command); } void formatedTextTarget(char *target, char *manHandler, char *baseDir) { char command[1000], *calls[] = { "groff", manHandler, NULL }; checkForCalls(calls); sprintf(command, "groff -man -Tascii -M%s/share/groff/tmac -F%s/share/groff/font %s | %s", baseDir, baseDir, target, manHandler); system(command); } void textTarget(char *target, char *manHandler, char *baseDir, char *tempDir, char *name) { char command[1000], *calls[] = { "groff", "sed", manHandler, NULL }; checkForCalls(calls); sprintf(command, "groff -man -Tascii -M%s/share/groff/tmac -F%s/share/groff/font %s | sed -e 's/_//g' | sed -e 's/.//g' > \"%s/%s.man\"; %s \"%s/%s.man\"; sleep 2; rm \"%s/%s.man\" &", baseDir, baseDir, target, tempDir, name, manHandler, tempDir, name, tempDir, name); system(command); } void checkForCalls(char *calls[]) { int i; /* If anybody can figure out how to check to see that a string ** ** actually calls a command, put the code here. */ /* for(i=0; calls[i]!=NULL; i++) */ } StringList_ptr initializedStringList_ptr(char *string){ StringList_ptr list_ptr = (StringList_ptr) malloc (sizeof(StringList)); list_ptr->string = (char*) malloc ((strlen(string)+1)*sizeof(char)); strcpy(list_ptr->string, string); list_ptr->next = NULL; return list_ptr; } void usage(char *commandName) { printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", "\n Usage: ", commandName, " [-m|--manonly|-i|--infoonly] \n\n", " Prerequisites:\n", " cygwinb19.dll\n", " bash, sed\n", " groff (if you want to view man pages via less or notepad;\n", " otherwise, set the environment variable MANHANDLER\n", " to the complete path of your web browser\n", " [i.e., 'c:\\Progra~1\\MSIE\\iexplore.exe'])\n\n", " Purpose:\n", " This is a 'man' emulator written for win32, cygnus style.\n", " It was originally based on 'man2html' by dj AT delorie DOT com,\n", " but its functionality is extended a bit. What it does is\n", " search through the directories in the environment variables\n", " INFOPATH and MANPATH for info and man pages, respectively,\n", " and if an info page is found, 'tkinfo' is called, otherwise\n", " the application named in the environment variable MANHANDLER\n", " (by default, 'less') is called to view the man page. If the\n", " MANHANDLER application contains the string 'iexplore' or\n", " 'netscape', then this program converts the man page to html\n", " format for the browser. If the MANHANDLER application name\n", " contains the string 'more' or 'less', the man page is piped\n", " through groff to be viewed appropriately. Otherwise (i.e.,\n", " if MANHANDLER is defined as 'notepad'), the page is piped\n", " through groff into plain text format.\n\n", " Author: Joseph Banta, Perceptics Corp., Knoxville, TN, June 1998\n", " joe_banta AT perceptics DOT com\n\n"); exit(0); } - For help on using this list (especially unsubscribing), send a message to "gnu-win32-request AT cygnus DOT com" with one line of text: "help".