/* * HelpPC Reference Library to HTML converter. Ver 1.02 * * Copyleft (l) Stanislav Sokolov (stanisls@gmail.com) * The source is published under GNU General Public Licence * ver. 2 of June 1991. * * Visit on-line version on: * http://heim.ifi.uio.no/~stanisls/helppc/ * * NOTE: If you want to make HTML version using UNIX, convert first * all TXT files so that they have UNIX-style new-line. * * Revision History * ---------------- * 2022 May 31 - Increased the maximum file path to 4095 bytes, and * removed a double free call. */ #include #include #include #include #include #include #define BASE_PATH "helppc" #define MAX_LINE_LENGTH 1024 #define MAX_FILENAME_LENGTH 100 #define MAX_PATH_LENGTH 4095 #define MAX_INDEX_TITLE_LENGTH 45 struct conv { char *file; char *orig; struct conv *next; }; struct conv *c_head; void cleanLine(char *); char *makeFile(char *); void rmFirst(char *); char *makeFName(char *); void convError(char *); void parseLine(char *); int buildConv(int argc, char **argv); void strlower(char *x); void freeTable(struct conv *); void idxSortWrt(struct conv[], int, FILE *); void _qsort(struct conv c_list[], int first, int last); int main(int argc, char **argv) { FILE *f = NULL, *fout = NULL; char *tmp, *fName; char foutname[MAX_FILENAME_LENGTH + 1]; int i; char first; int pending = 0, files; fprintf(stderr, "\nHelpPC Reference Library to HTML converter. Ver 1.02\n" "Copyleft (l) Stanislav Sokolov\n\n"); if (argc == 1) { fprintf(stderr, "Usage:\n %s [file.txt [file2.txt [...]]]\n" "or\n %s *.txt\n\n", argv[0], argv[0]); return 10; } mkdir(BASE_PATH, 0777); /* Build conversion table */ fprintf(stderr, "Building conversion table and index files...\n"); i = buildConv(argc, argv); fprintf(stderr, "Found %d keywords corresponding to %d unique entries.\n", i >> 16, i & 0xFFFF); /* Parse the files */ for (files = 1; files < argc; files++) { // For all files given to function char line[MAX_LINE_LENGTH + 1] = {0}; if ((f = fopen(argv[files], "r")) == NULL) { fprintf(stderr, "Error opening %s\n", argv[files]); exit(100); } fprintf(stderr, "\nParsing %s...\n", argv[files]); while (fgets(line, MAX_LINE_LENGTH, f) != NULL) { cleanLine(line); if (strnlen(line, MAX_LINE_LENGTH)) { line[strnlen(line, MAX_LINE_LENGTH) - 1] = '\0'; // Strip the newline } first = line[0]; if (first == '@') { /* do nothing */ } else if (first == ':') { /*Make new html file*/ if (pending) { fprintf(fout, "\n\n\n"); fclose(fout); } pending = 1; rmFirst(line); tmp = strtok(line, ":"); fName = makeFName(tmp); sprintf(foutname, "%s/%s", BASE_PATH, fName); if ((fout = fopen(foutname, "w+")) == NULL) { convError("Cannot open for writing!"); convError(foutname); return 3; } free(fName); fprintf(fout, "\n\n%s\n\n\n\n
",
                tmp);

      } else if (first == '^') { /* Make H2 */
        rmFirst(line);
        fprintf(fout, "
\n\n

%s

\n\n
\n", line);
        continue;
      } else if (first == '%') { /* Make Bold */
        rmFirst(line);
        fprintf(fout, "%s\n", line);
      } else {
        if (line[0] != '\0') {
          parseLine(line);
        }
        fprintf(fout, "%s\n", line);
      }

    } /* while */
    fclose(f);
  } /* for */

  fprintf(stderr, "\nAll done.\n");

  if (pending) {
    fprintf(fout, "
\n\n\n"); fclose(fout); } freeTable(c_head); return 0; } void cleanLine(char *x) { char const tgr_o[] = {'³', 'À', 'Ä', 'Ú', 'Å', 'Ù', '´', 'Â', 'Á', '¿', 'Ã', 'ù', '\0'}; char const tgr_r[] = {'|', '`', '-', '´', '+', '\'', '|', '-', '-', '.', '|', '/', '\0'}; /* Remove newline at the en of the string returned by fgets() */ if (strnlen(x, MAX_LINE_LENGTH)) { x[strnlen(x, MAX_LINE_LENGTH) - 1] = '\0'; } // Convert to Latin-1 encoding while (*x != '\0') { for (int i = 0; tgr_o[i] != '\0'; i++) { if (*x == tgr_o[i]) *x = tgr_r[i]; } x++; } /* Remove DOS-style newline*/ if (strrchr(x, 0x0D) != NULL) { *strrchr(x, 0x0D) = 0x0; } } void rmFirst(char *x) { memmove(x, x + 1, strnlen(x, MAX_LINE_LENGTH)); } char *makeFName(char *x) { char *tmp = malloc(strnlen(x, MAX_FILENAME_LENGTH) + 6); char *o = tmp; while ((*tmp = *x)) { *tmp = tolower(*tmp); if (*tmp == ' ') *tmp = '_'; else if (*tmp == ',') *tmp = '-'; else if (*tmp == '.') *tmp = '_'; else if (*tmp == '(') *tmp = '-'; else if (*tmp == ')') *tmp = '-'; tmp++; x++; } strcat(o, ".html"); return o; } void convError(char *x) { fprintf(stderr, "\n Error: %s\n", x); } void parseLine(char *x) { char *tmp = malloc(MAX_LINE_LENGTH + 1); char *t_o = tmp; char *x_o = x; char link[50]; char *l_dup; int i, found; struct conv *conv; memset(tmp, 0, MAX_LINE_LENGTH); while (*x != '\0') { if (*x == '~' && *(x + 1) != '~') { x++; // Remove the ~ // Copy each letter until the trailing ~ i = 0; while (*x != '~' && *x != '\0') { link[i++] = *x; x++; } link[i] = '\0'; /* Do an exact matching first... */ conv = c_head; l_dup = strdup(link); strlower(l_dup); found = 0; while (conv != NULL) { if (strcmp(conv->orig, l_dup) == 0) { found = 1; break; } conv = conv->next; } /* As not all keywords and link texts match exactly, do two more relaxed searches in succession. */ if (!found) { conv = c_head; while (conv != NULL) { if (strncmp(conv->orig, l_dup, strnlen(l_dup, MAX_LINE_LENGTH)) == 0) { found = 1; break; } conv = conv->next; } if (!found) { conv = c_head; while (conv != NULL) { if (strstr(conv->orig, l_dup) != NULL) { break; } conv = conv->next; } } } if (conv == NULL) { fprintf(stderr, " Warning: No translation for %s\n", l_dup); strcat(tmp, link); } else { strcat(tmp, "file); strcat(tmp, "\">"); strcat(tmp, link); strcat(tmp, ""); } free(l_dup); while (*tmp != '\0') tmp++; tmp--; } else { if (*x == '~') x++; *tmp = *x; } tmp++; x++; } *tmp = '\0'; strcpy(x_o, t_o); free(t_o); } int buildConv(int argc, char **argv) { int files, count = 0, i, entry = 0; FILE *f, *fidx, *idx; char indexTitle[MAX_INDEX_TITLE_LENGTH + 1]; char fidxName[MAX_FILENAME_LENGTH + 1]; char path[MAX_PATH_LENGTH + 1]; char *tmp, *fName = NULL, *x1, *x2; struct conv *conv = NULL; struct conv c_list[2000]; int c_list_len; /* Start main index */ sprintf(path, "%s/index.html", BASE_PATH); if ((idx = fopen(path, "w+")) == NULL) { convError("Error writing main index!"); exit(100); } fprintf(idx, "\n\n HelpPC Reference Library\n" "\n\n\n
\n

HelpPC Reference Library

\n" "David Jurgens
\n
\n
\n" "
\n\nTopics:
    \n"); for (files = 1; files < argc; files++) { // For all files given to function if ((f = fopen(argv[files], "r")) == NULL) { fprintf(stderr, "Error opening %s\n", argv[files]); exit(100); } /* Get index title */ fprintf(stderr, " Getting index title of %s:\n\t", argv[files]); fgets(indexTitle, MAX_INDEX_TITLE_LENGTH, f); cleanLine(indexTitle); if (indexTitle[0] != '@') { convError("Not a valid help file!"); exit(1); } rmFirst(indexTitle); x1 = strdup(indexTitle); tmp = strtok(x1, " /,"); x2 = makeFName(tmp); free(x1); fprintf(stderr, "%s\n\n", indexTitle); fprintf(idx, "
  • %s\n", x2, indexTitle); sprintf(fidxName, "%s/idx_%s", BASE_PATH, x2); if ((fidx = fopen(fidxName, "w+")) == NULL) { convError("Error writing topic index!"); convError(x2); free(x2); exit(100); } free(x2); fprintf(fidx, "\n\n %s\n\n\n\n\n", indexTitle); fprintf(fidx, "

    %s

    \n\n", indexTitle); fprintf(fidx, "\n\n"); c_list_len = 0; char line[MAX_LINE_LENGTH + 1] = {0}; while (fgets(line, MAX_LINE_LENGTH, f) != NULL) { cleanLine(line); if (line[0] == ':') { line[strnlen(line, MAX_LINE_LENGTH) - 1] = '\0'; // Strip the newline rmFirst(line); // Strip the : /* Make file name */ tmp = strtok(line, ":"); if (tmp != NULL) { fName = makeFName(tmp); entry++; } while (tmp != NULL) { count++; size_t const filenameLength = strnlen(fName, MAX_FILENAME_LENGTH); size_t const nameLength = strnlen(tmp, MAX_FILENAME_LENGTH); c_list[c_list_len].file = malloc(filenameLength + 1); c_list[c_list_len].orig = malloc(nameLength + 1); strcpy(c_list[c_list_len].file, fName); strcpy(c_list[c_list_len].orig, tmp); c_list_len++; conv = malloc(sizeof(struct conv)); conv->file = malloc(filenameLength + 1); conv->file[filenameLength] = '\0'; conv->orig = malloc(strnlen(tmp, nameLength) + 1); conv->orig[nameLength] = '\0'; conv->next = c_head; c_head = conv; strncpy(conv->file, fName, filenameLength); strlower(tmp); strncpy(conv->orig, tmp, nameLength); tmp = strtok(NULL, ":"); } free(fName); } /* if */ } /* while */ idxSortWrt(c_list, c_list_len, fidx); for (i = 0; i < c_list_len; i++) { free(c_list[i].file); free(c_list[i].orig); } fprintf(fidx, "\n\n
    \n\n"); fclose(f); fclose(fidx); } /* for */ fprintf(idx, "
\n
This site is a HTML version of HelpPC Reference Library, " "a DOS-based hypertext program by David Jurgens. The conversion " "program was written by Stanislav Sokolov and the " "C source is available for download under GNU General Public Licence." "
\n\n"); fclose(idx); return (count << 16) | (entry & 0xFFFF); } void strlower(char *x) { while (*x != '\0') { *x = tolower(*x); x++; } } void freeTable(struct conv *conv) { struct conv *x; while (conv != NULL) { free(conv->file); free(conv->orig); x = conv; conv = conv->next; free(x); } } void idxSortWrt(struct conv c_list[], int len, FILE *fidx) { int i; _qsort(c_list, 0, len - 1); for (i = 0; i < len; i++) { fprintf(fidx, " %s ", c_list[i].file, c_list[i].orig); if ((i + 1) % 5 == 0) fprintf(fidx, "\n\n\n"); } } /* Text with leading underscore is compared from the next character */ void _qsort(struct conv c_list[], int first, int last) { int i = first, j = last; char *med; struct conv swap; med = c_list[(first + last) / 2].orig; if (*med == '_') { med++; } do { while (strcasecmp( (*c_list[i].orig == '_' ? c_list[i].orig + 1 : c_list[i].orig), med) < 0) { i++; } while (strcasecmp(med, *c_list[j].orig == '_' ? c_list[j].orig + 1 : c_list[j].orig) < 0) { j--; } if (i <= j) { swap = c_list[i]; c_list[i++] = c_list[j]; c_list[j--] = swap; } } while (i <= j); if (first < j) _qsort(c_list, first, j); if (i < last) _qsort(c_list, i, last); }