home *** CD-ROM | disk | FTP | other *** search
/ Media Depot 5 / mediadepotvolume51993.iso / FILES / 13 / FAQ202S.ZIP / faq / htmsplit.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-22  |  10.4 KB  |  409 lines

  1. /*
  2.  * Splits an HTML file into several files and updates
  3.  * hypertext links accordingly.
  4.  *
  5.  * Restrictions:
  6.  *
  7.  *   1. The ``<A NAME=...>'' and ``<A HREF=...>''
  8.  *      anchors MUST be found verbatim, i.e. without
  9.  *      excess whitespace and *not* split between 2
  10.  *      adjacent lines.  If the HTML file was produced
  11.  *      by Makeinfo, you should use the @w{} directive
  12.  *      judiciously to prevent line-filling mechanism
  13.  *      from splitting the anchors between lines.
  14.  *   2. Currently only supports splitting the file one
  15.  *      node per file; you cannot split the file by
  16.  *      chapters.  The string which signals the beginning
  17.  *      of a new node is hard-wired into the program and
  18.  *      cannot be changed without recompiling.
  19.  *      
  20.  *
  21.  * Author: Eli Zaretskii <eliz@is.elta.co.il>
  22.  *
  23.  * Version: 1.1
  24.  *
  25.  * Last updated: 22 June, 1996
  26.  *
  27.  * ----------------------------------------------------------
  28.  *
  29.  * You can do whatever you like with this program, except:
  30.  * (1) preventing other people (including the author) do
  31.  * whatever they like, and (2) removing the author and
  32.  * version info above.
  33.  *
  34.  * ----------------------------------------------------------
  35.  *
  36.  */
  37.  
  38. #include <stdio.h>
  39. #include <unistd.h>
  40. #include <stdlib.h>
  41. #include <string.h>
  42. #include <errno.h>
  43. #include <fcntl.h>
  44.  
  45. #ifdef  __DJGPP__
  46.  
  47. #include <io.h>
  48.  
  49. /* Make so our start-up code is minimal: disable filename
  50.    globbing, and don't load environment file.  */
  51. #include <crt0.h>
  52.  
  53. char ** __crt0_glob_function(char *arg) { return (char **)0; }
  54. void   __crt0_load_environment_file(char *app_name) {}
  55.  
  56. #else    /* not __DJGPP__ */
  57.  
  58. /* Some Unix boxes don't have functon prototypes on the header files.
  59.    -Wall will complain about this, so here are the prototypes:  */
  60.  
  61. void perror (const char *);
  62. int  fprintf(FILE *, const char *, ...);
  63.  
  64. /* Non-DJGPP libraries might not have these two functions.  */
  65.  
  66. #include <ctype.h>
  67.  
  68. int
  69. strnicmp(const char *s1, const char *s2, size_t n)
  70. {
  71.  
  72.   if (n == 0)
  73.     return 0;
  74.   do {
  75.     if (tolower(*s1) != tolower(*s2++))
  76.       return (int)tolower(*s1) - (int)tolower(*--s2);
  77.     if (*s1++ == 0)
  78.       break;
  79.   } while (--n != 0);
  80.   return 0;
  81. }
  82.  
  83. #include <sys/types.h>
  84. #include <sys/stat.h>
  85.  
  86. long
  87. filelength(int fd)
  88. {
  89.   struct stat stbuf;
  90.  
  91.   if (fstat(fd, &stbuf) == 0)
  92.     return stbuf.st_size;
  93.  
  94.   return -1;
  95. }
  96.  
  97. #endif  /* not __DJGPP__ */
  98.  
  99. #ifndef O_BINARY
  100. #define O_BINARY    0
  101. #endif
  102.  
  103. static const char split_marker[] = "<P> | <A HREF=\"#";
  104. static const char dest_marker[]  = "<A NAME=\"";
  105. static const char link_marker[]  = "<A HREF=\"#";
  106. static size_t split_marker_len = sizeof(split_marker) -1;
  107. static size_t dest_marker_len  = sizeof(dest_marker) - 1;
  108. static size_t link_marker_len  = sizeof(link_marker) - 1;
  109.  
  110. /* Is POINT at the first character of STRING whose length is LEN?  */
  111. int
  112. looking_at(const char string[], size_t len, char *point)
  113. {
  114.   return strnicmp(string, point, len) == 0;
  115. }
  116.  
  117. /* Record a position where we'll split the file, bump point.  */
  118. static int *split_pos_table;        /* table of split positions */
  119. static int  split_pos_table_size;   /* the size of the table */
  120. static int  split_pos_idx;          /* index of next free slot */
  121.  
  122. size_t
  123. remember_split_pos(size_t pos)
  124. {
  125.   if (split_pos_idx >= split_pos_table_size)
  126.     {
  127.       if (split_pos_table)
  128.         split_pos_table =
  129.           (int *)realloc(split_pos_table,
  130.                          (split_pos_table_size *= 2)*sizeof(size_t));
  131.       else
  132.         {
  133.           split_pos_table_size = 100;
  134.           split_pos_table = (int *)malloc(split_pos_table_size*sizeof(size_t));
  135.         }
  136.  
  137.       if (split_pos_table == (int *)0)
  138.         {
  139.           errno = ENOMEM;
  140.           perror("split_pos table");
  141.           exit(2);
  142.         }
  143.     }
  144.  
  145.   split_pos_table[split_pos_idx++] = pos;
  146.  
  147.   return split_marker_len;
  148. }
  149.  
  150. /* Return the file position where subfile FILENO ends.  */
  151. size_t
  152. get_split_pos(int fileno)
  153. {
  154.   return split_pos_table[fileno];
  155. }
  156.  
  157. /* Record an anchor name and its subfile number, bump point.  */
  158. struct _dest_pos {
  159.   char *name;
  160.   int   fileno;
  161. };
  162. static struct _dest_pos *dest_pos_table;        /* table of anchors */
  163. static int               dest_pos_table_size;   /* the size of the table */
  164. static int               dest_pos_idx;          /* index of next free slot */
  165.  
  166. int
  167. remember_dest_pos(char *p, int fileno)
  168. {
  169.   char *save_point = p;
  170.   char *name_start;
  171.  
  172.   if (dest_pos_idx >= dest_pos_table_size)
  173.     {
  174.       if (dest_pos_table)
  175.         dest_pos_table = (struct _dest_pos *)
  176.           realloc(dest_pos_table,
  177.                   (dest_pos_table_size *= 2)*sizeof(struct _dest_pos));
  178.       else
  179.         {
  180.           dest_pos_table_size = 100;
  181.           dest_pos_table = (struct _dest_pos *)
  182.             malloc(dest_pos_table_size*sizeof(struct _dest_pos));
  183.         }
  184.  
  185.       if (dest_pos_table == (struct _dest_pos *)0)
  186.         {
  187.           errno = ENOMEM;
  188.           perror("dest_pos table");
  189.           exit(2);
  190.         }
  191.     }
  192.  
  193.   p += dest_marker_len;
  194.   name_start = p;
  195.   while (*p !='"')
  196.     p++;
  197.  
  198.   dest_pos_table[dest_pos_idx].fileno = fileno;
  199.   dest_pos_table[dest_pos_idx].name = (char *)malloc(p - name_start + 1);
  200.   if (dest_pos_table[dest_pos_idx].name == (char *)0)
  201.     {
  202.       errno = ENOMEM;
  203.       perror("name in dest_pos table");
  204.       exit(2);
  205.     }
  206.   strncpy(dest_pos_table[dest_pos_idx].name, name_start, p - name_start);
  207.   dest_pos_table[dest_pos_idx++].name[p - name_start] = '\0';
  208.  
  209.   return p - save_point;
  210. }
  211.  
  212. /* Skip ``<A HREF="'', return pointer to beginning of anchor name.  */
  213. char *
  214. skip_until_anchor_name(char *point)
  215. {
  216.   return point + link_marker_len;
  217. }
  218.  
  219. /* Which subfile is this anchor in?  */
  220. int
  221. subfile_num_for_anchor_at_point(char *point)
  222. {
  223.   char c, *name_start = point;
  224.   int idx = 0;
  225.  
  226.   while (*point != '"')
  227.     point++;
  228.  
  229.   for (c = *name_start; idx < dest_pos_idx; idx++)
  230.     {
  231.       register char *anchor = dest_pos_table[idx].name;
  232.  
  233.       if (anchor[0] == c)
  234.         {
  235.       size_t len = strlen(anchor);
  236.  
  237.       /* Be careful not to catch possible substrings!  */
  238.           if (len == point - name_start
  239.           && strncmp(anchor, name_start, len) == 0)
  240.  
  241.             return dest_pos_table[idx].fileno;
  242.         }
  243.     }
  244.  
  245.   fprintf(stderr, "%.*s: not found in table of anchors\n",
  246.           (int)(point - name_start), name_start);
  247.   exit(2);
  248. }
  249.  
  250. int
  251. main(int argc, char *argv[])
  252. {
  253.   if (argc == 3)
  254.     {
  255.       int in_fd = open(argv[1], O_RDONLY | O_BINARY);
  256.       int out_fd;
  257.       long fsize, actual_size;
  258.       char *in_file;
  259.       char *p, *last_p, *from;
  260.       int subfile = 0;
  261.       char subfile_name[FILENAME_MAX];
  262.       int max_digits = 1;
  263.       size_t split_pos;
  264.  
  265.       /* First, read the file. */
  266.       
  267.       if (in_fd < 0)
  268.         {
  269.           perror(argv[1]);
  270.           return 2;
  271.         }
  272.  
  273.       fsize = filelength(in_fd);
  274.  
  275.       in_file = (char *)malloc(fsize + 1);      /* leave place for `\0' */
  276.       if (in_file == (char *)0)
  277.         {
  278.           errno = ENOMEM;
  279.           perror(argv[1]);
  280.           return 2;
  281.         }
  282.  
  283.       if ((actual_size = read(in_fd, in_file, fsize)) != fsize)
  284.         {
  285.           if (actual_size <= 0)
  286.             {
  287.               perror(argv[1]);
  288.               return 2;
  289.             }
  290.           fprintf(stderr, "%s: size is %ld, but only %ld bytes read\n",
  291.                           argv[1], fsize, actual_size);
  292.           fsize = actual_size;
  293.         }
  294.  
  295.       close (in_fd);
  296.  
  297.       for (p = in_file + fsize - 1; *p == 0x1a && p > in_file; --p)
  298.         {
  299.           fsize--;
  300.           actual_size--;
  301.         }
  302.  
  303.       if (fsize < 2048)
  304.         {
  305.           fprintf(stderr, "%s: too small to bother\n", argv[1]);
  306.           return 3;
  307.         }
  308.  
  309.       p[1] = '\0';
  310.  
  311.       /* Pass 1: Determine the file positions where the file
  312.                  will be split, and remember positions of the
  313.                  <A NAME="#dest"> destination anchors.  */
  314.  
  315.       for (last_p = p, p = in_file; p < last_p; )
  316.         {
  317.           if (*p == '\n' && looking_at(split_marker, split_marker_len, ++p))
  318.             {
  319.               p += remember_split_pos(p - in_file);
  320.               subfile++;
  321.             }
  322.           else if (looking_at(dest_marker, dest_marker_len, p))
  323.             {
  324.               p += remember_dest_pos(p, subfile);
  325.             }
  326.           else
  327.             ++p;
  328.         }
  329.  
  330.       /* Last subfile ends at EOF.  */
  331.       remember_split_pos(p - in_file);
  332.       subfile++;
  333.  
  334.       while (subfile /= 10)
  335.         max_digits++;
  336.  
  337.       /* Pass 2: Generate the subfiles with updated links.  */
  338.  
  339.       sprintf(subfile_name, "%s.html", argv[2]);
  340.       if ((out_fd = open(subfile_name,
  341.                          O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)) == -1)
  342.         {
  343.           perror(subfile_name);
  344.           return 2;
  345.         }
  346.       split_pos = get_split_pos(subfile);
  347.  
  348.       for (p = in_file, from = p; p < last_p; ++p)
  349.         {
  350.           if (p - in_file >= split_pos)     /* time to start another file */
  351.             {
  352.               if (write(out_fd, from, split_pos - (from - in_file)) <= 0)
  353.                 {
  354.                   perror("write at split position");
  355.                   return 2;
  356.                 }
  357.               close(out_fd);
  358.               from = in_file + split_pos;
  359.               split_pos = get_split_pos(++subfile);
  360.               sprintf(subfile_name, "%s%.*d.html",
  361.                       argv[2], max_digits, subfile);
  362.               if ((out_fd = open(subfile_name,
  363.                  O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
  364.                  0666)) == -1)
  365.                 {
  366.                   perror(subfile_name);
  367.                   return 2;
  368.                 }
  369.             }
  370.           else if (looking_at(link_marker, link_marker_len, p))
  371.             {
  372.               int which_file;
  373.  
  374.               p = skip_until_anchor_name(p);
  375.               which_file = subfile_num_for_anchor_at_point(p);
  376.  
  377.               --p;  /* the `#' character goes AFTER the file */
  378.  
  379.               sprintf(subfile_name, which_file ? "%s%.*d.html" : "%s.html",
  380.                       argv[2], max_digits, which_file);
  381.               if (write(out_fd, from, p - from) <= 0 ||
  382.                  write(out_fd, subfile_name, strlen(subfile_name)) <= 0)
  383.                 {
  384.                   perror("write at anchor name");
  385.                   return 2;
  386.                 }
  387.               from = p;
  388.             }
  389.         }
  390.  
  391.       if (p != from)
  392.         if (write(out_fd, from, p - from) <= 0)
  393.           {
  394.             perror("write at EOF");
  395.             return 2;
  396.           }
  397.  
  398.       fprintf(stderr, "%s was split into %d file%s\n",
  399.               argv[1], subfile + 1, subfile ? "s" : "");
  400.  
  401.       return 0;
  402.     }
  403.   else
  404.     {
  405.       fprintf(stderr, "Usage: %s inputfile outbase\n", *argv);
  406.       return 1;
  407.     }
  408. }
  409.