]> sjero.net Git - wget/blobdiff - src/html-parse.c
[svn] Remove K&R support.
[wget] / src / html-parse.c
index 88aec75341f7d0553af4b83843eff698817a39ea..4a0a525771a97d173ee10bd713741e2c5f7a11f4 100644 (file)
@@ -96,11 +96,7 @@ so, delete this exception statement from your version.  */
 
 #include <stdio.h>
 #include <stdlib.h>
 
 #include <stdio.h>
 #include <stdlib.h>
-#ifdef HAVE_STRING_H
-# include <string.h>
-#else
-# include <strings.h>
-#endif
+#include <string.h>
 #include <assert.h>
 
 #include "wget.h"
 #include <assert.h>
 
 #include "wget.h"
@@ -257,33 +253,119 @@ struct pool {
     }                                                                          \
 } while (0)
 \f
     }                                                                          \
 } while (0)
 \f
-#define AP_DOWNCASE            1
-#define AP_PROCESS_ENTITIES    2
-#define AP_TRIM_BLANKS         4
+/* Test whether n+1-sized entity name fits in P.  We don't support
+   IE-style non-terminated entities, e.g. "&ltfoo" -> "<foo".
+   However, "&lt;foo" will work, as will "&lt!foo", "&lt", etc.  In
+   other words an entity needs to be terminated by either a
+   non-alphanumeric or the end of string.  */
+#define FITS(p, n) (p + n == end || (p + n < end && !ISALNUM (p[n])))
+
+/* Macros that test entity names by returning true if P is followed by
+   the specified characters.  */
+#define ENT1(p, c0) (FITS (p, 1) && p[0] == c0)
+#define ENT2(p, c0, c1) (FITS (p, 2) && p[0] == c0 && p[1] == c1)
+#define ENT3(p, c0, c1, c2) (FITS (p, 3) && p[0]==c0 && p[1]==c1 && p[2]==c2)
+
+/* Increment P by INC chars.  If P lands at a semicolon, increment it
+   past the semicolon.  This ensures that e.g. "&lt;foo" is converted
+   to "<foo", but "&lt,foo" to "<,foo".  */
+#define SKIP_SEMI(p, inc) (p += inc, p < end && *p == ';' ? ++p : p)
+
+/* Decode the HTML character entity at *PTR, considering END to be end
+   of buffer.  It is assumed that the "&" character that marks the
+   beginning of the entity has been seen at *PTR-1.  If a recognized
+   ASCII entity is seen, it is returned, and *PTR is moved to the end
+   of the entity.  Otherwise, -1 is returned and *PTR left unmodified.
+
+   The recognized entities are: &lt, &gt, &amp, &apos, and &quot.  */
+
+static int
+decode_entity (const char **ptr, const char *end)
+{
+  const char *p = *ptr;
+  int value = -1;
+
+  if (++p == end)
+    return -1;
+
+  switch (*p++)
+    {
+    case '#':
+      /* Process numeric entities "&#DDD;" and "&#xHH;".  */
+      {
+       int digits = 0;
+       value = 0;
+       if (*p == 'x')
+         for (++p; value < 256 && p < end && ISXDIGIT (*p); p++, digits++)
+           value = (value << 4) + XDIGIT_TO_NUM (*p);
+       else
+         for (; value < 256 && p < end && ISDIGIT (*p); p++, digits++)
+           value = (value * 10) + (*p - '0');
+       if (!digits)
+         return -1;
+       /* Don't interpret 128+ codes and NUL because we cannot
+          portably reinserted them into HTML.  */
+       if (!value || (value & ~0x7f))
+         return -1;
+       *ptr = SKIP_SEMI (p, 0);
+       return value;
+      }
+    /* Process named ASCII entities.  */
+    case 'g':
+      if (ENT1 (p, 't'))
+       value = '>', *ptr = SKIP_SEMI (p, 1);
+      break;
+    case 'l':
+      if (ENT1 (p, 't'))
+       value = '<', *ptr = SKIP_SEMI (p, 1);
+      break;
+    case 'a':
+      if (ENT2 (p, 'm', 'p'))
+       value = '&', *ptr = SKIP_SEMI (p, 2);
+      else if (ENT3 (p, 'p', 'o', 's'))
+       /* handle &apos for the sake of the XML/XHTML crowd. */
+       value = '\'', *ptr = SKIP_SEMI (p, 3);
+      break;
+    case 'q':
+      if (ENT3 (p, 'u', 'o', 't'))
+       value = '\"', *ptr = SKIP_SEMI (p, 3);
+      break;
+    }
+  return value;
+}
+#undef ENT1
+#undef ENT2
+#undef ENT3
+#undef FITS
+#undef SKIP_SEMI
+
+enum {
+  AP_DOWNCASE          = 1,
+  AP_DECODE_ENTITIES   = 2,
+  AP_TRIM_BLANKS       = 4
+};
 
 /* Copy the text in the range [BEG, END) to POOL, optionally
    performing operations specified by FLAGS.  FLAGS may be any
 
 /* Copy the text in the range [BEG, END) to POOL, optionally
    performing operations specified by FLAGS.  FLAGS may be any
-   combination of AP_DOWNCASE, AP_PROCESS_ENTITIES and AP_TRIM_BLANKS
+   combination of AP_DOWNCASE, AP_DECODE_ENTITIES and AP_TRIM_BLANKS
    with the following meaning:
 
    * AP_DOWNCASE -- downcase all the letters;
 
    with the following meaning:
 
    * AP_DOWNCASE -- downcase all the letters;
 
-   * AP_PROCESS_ENTITIES -- process the SGML entities and write out
-   the decoded string.  Recognized entities are &lt, &gt, &amp, &quot,
-   &nbsp and the numerical entities.
+   * AP_DECODE_ENTITIES -- decode the named and numeric entities in
+     the ASCII range when copying the string.
 
    * AP_TRIM_BLANKS -- ignore blanks at the beginning and at the end
 
    * AP_TRIM_BLANKS -- ignore blanks at the beginning and at the end
-   of text.  */
+     of text, as well as embedded newlines.  */
 
 static void
 convert_and_copy (struct pool *pool, const char *beg, const char *end, int flags)
 {
   int old_tail = pool->tail;
 
 static void
 convert_and_copy (struct pool *pool, const char *beg, const char *end, int flags)
 {
   int old_tail = pool->tail;
-  int size;
 
 
-  /* First, skip blanks if required.  We must do this before entities
-     are processed, so that blanks can still be inserted as, for
-     instance, `&#32;'.  */
+  /* Skip blanks if required.  We must do this before entities are
+     processed, so that blanks can still be inserted as, for instance,
+     `&#32;'.  */
   if (flags & AP_TRIM_BLANKS)
     {
       while (beg < end && ISSPACE (*beg))
   if (flags & AP_TRIM_BLANKS)
     {
       while (beg < end && ISSPACE (*beg))
@@ -291,9 +373,8 @@ convert_and_copy (struct pool *pool, const char *beg, const char *end, int flags
       while (end > beg && ISSPACE (end[-1]))
        --end;
     }
       while (end > beg && ISSPACE (end[-1]))
        --end;
     }
-  size = end - beg;
 
 
-  if (flags & AP_PROCESS_ENTITIES)
+  if (flags & AP_DECODE_ENTITIES)
     {
       /* Grow the pool, then copy the text to the pool character by
         character, processing the encountered entities as we go
     {
       /* Grow the pool, then copy the text to the pool character by
         character, processing the encountered entities as we go
@@ -304,78 +385,25 @@ convert_and_copy (struct pool *pool, const char *beg, const char *end, int flags
         never lengthen it.  */
       const char *from = beg;
       char *to;
         never lengthen it.  */
       const char *from = beg;
       char *to;
+      int squash_newlines = flags & AP_TRIM_BLANKS;
 
       POOL_GROW (pool, end - beg);
       to = pool->contents + pool->tail;
 
       while (from < end)
        {
 
       POOL_GROW (pool, end - beg);
       to = pool->contents + pool->tail;
 
       while (from < end)
        {
-         if (*from != '&')
-           *to++ = *from++;
-         else
+         if (*from == '&')
            {
            {
-             const char *save = from;
-             int remain;
-
-             if (++from == end)
-               goto lose;
-             remain = end - from;
-
-             /* Process numeric entities "&#DDD;" and "&#xHH;".  */
-             if (*from == '#')
-               {
-                 int numeric = 0, digits = 0;
-                 ++from;
-                 if (*from == 'x')
-                   {
-                     ++from;
-                     for (; from < end && ISXDIGIT (*from); from++, digits++)
-                       numeric = (numeric << 4) + XDIGIT_TO_NUM (*from);
-                   }
-                 else
-                   {
-                     for (; from < end && ISDIGIT (*from); from++, digits++)
-                       numeric = (numeric * 10) + (*from - '0');
-                   }
-                 if (!digits)
-                   goto lose;
-                 numeric &= 0xff;
-                 *to++ = numeric;
-               }
-#define FROB(x) (remain >= (sizeof (x) - 1)                    \
-                && 0 == memcmp (from, x, sizeof (x) - 1)       \
-                && (*(from + sizeof (x) - 1) == ';'            \
-                    || remain == sizeof (x) - 1                \
-                    || !ISALNUM (*(from + sizeof (x) - 1))))
-             else if (FROB ("lt"))
-               *to++ = '<', from += 2;
-             else if (FROB ("gt"))
-               *to++ = '>', from += 2;
-             else if (FROB ("amp"))
-               *to++ = '&', from += 3;
-             else if (FROB ("quot"))
-               *to++ = '\"', from += 4;
-             /* We don't implement the proposed "Added Latin 1"
-                entities (except for nbsp), because it is unnecessary
-                in the context of Wget, and would require hashing to
-                work efficiently.  */
-             else if (FROB ("nbsp"))
-               *to++ = 160, from += 4;
+             int entity = decode_entity (&from, end);
+             if (entity != -1)
+               *to++ = entity;
              else
              else
-               goto lose;
-#undef FROB
-             /* If the entity was followed by `;', we step over the
-                `;'.  Otherwise, it was followed by either a
-                non-alphanumeric or EOB, in which case we do nothing.  */
-             if (from < end && *from == ';')
-               ++from;
-             continue;
-
-           lose:
-             /* This was not an entity after all.  Back out.  */
-             from = save;
-             *to++ = *from++;
+               *to++ = *from++;
            }
            }
+         else if ((*from == '\n' || *from == '\r') && squash_newlines)
+           ++from;
+         else
+           *to++ = *from++;
        }
       /* Verify that we haven't exceeded the original size.  (It
         shouldn't happen, hence the assert.)  */
        }
       /* Verify that we haven't exceeded the original size.  (It
         shouldn't happen, hence the assert.)  */
@@ -462,7 +490,7 @@ advance_declaration (const char *beg, const char *end)
     AC_S_DASH4,
     AC_S_QUOTE1,
     AC_S_IN_QUOTE,
     AC_S_DASH4,
     AC_S_QUOTE1,
     AC_S_IN_QUOTE,
-    AC_S_QUOTE2,
+    AC_S_QUOTE2
   } state = AC_S_BANG;
 
   if (beg == end)
   } state = AC_S_BANG;
 
   if (beg == end)
@@ -698,17 +726,15 @@ static int tag_backout_count;
    MAPFUN will be called with two arguments: pointer to an initialized
    struct taginfo, and MAPARG.
 
    MAPFUN will be called with two arguments: pointer to an initialized
    struct taginfo, and MAPARG.
 
-   ALLOWED_TAG_NAMES should be a NULL-terminated array of tag names to
-   be processed by this function.  If it is NULL, all the tags are
-   allowed.  The same goes for attributes and ALLOWED_ATTRIBUTE_NAMES.
+   ALLOWED_TAGS and ALLOWED_ATTRIBUTES are hash tables the keys of
+   which are the tags and attribute names that this function should
+   use.  If ALLOWED_TAGS is NULL, all tags are processed; if
+   ALLOWED_ATTRIBUTES is NULL, all attributes are returned.
 
    (Obviously, the caller can filter out unwanted tags and attributes
    just as well, but this is just an optimization designed to avoid
 
    (Obviously, the caller can filter out unwanted tags and attributes
    just as well, but this is just an optimization designed to avoid
-   unnecessary copying for tags/attributes which the caller doesn't
-   want to know about.  These lists are searched linearly; therefore,
-   if you're interested in a large number of tags or attributes, you'd
-   better set these to NULL and filter them out yourself with a
-   hashing process most appropriate for your application.)  */
+   unnecessary copying of tags/attributes which the caller doesn't
+   care about.)  */
 
 void
 map_html_tags (const char *text, int size,
 
 void
 map_html_tags (const char *text, int size,
@@ -897,7 +923,7 @@ map_html_tags (const char *text, int size,
                  goto look_for_tag;
                attr_raw_value_end = p; /* <foo bar="baz"> */
                                        /*               ^ */
                  goto look_for_tag;
                attr_raw_value_end = p; /* <foo bar="baz"> */
                                        /*               ^ */
-               operation = AP_PROCESS_ENTITIES;
+               operation = AP_DECODE_ENTITIES;
                if (flags & MHT_TRIM_VALUES)
                  operation |= AP_TRIM_BLANKS;
              }
                if (flags & MHT_TRIM_VALUES)
                  operation |= AP_TRIM_BLANKS;
              }
@@ -920,7 +946,7 @@ map_html_tags (const char *text, int size,
                  goto backout_tag;
                attr_raw_value_begin = attr_value_begin;
                attr_raw_value_end = attr_value_end;
                  goto backout_tag;
                attr_raw_value_begin = attr_value_begin;
                attr_raw_value_end = attr_value_end;
-               operation = AP_PROCESS_ENTITIES;
+               operation = AP_DECODE_ENTITIES;
              }
          }
        else
              }
          }
        else
@@ -984,8 +1010,7 @@ map_html_tags (const char *text, int size,
       taginfo.attrs = pairs;
       taginfo.start_position = tag_start_position;
       taginfo.end_position   = p + 1;
       taginfo.attrs = pairs;
       taginfo.start_position = tag_start_position;
       taginfo.end_position   = p + 1;
-      /* Ta-dam! */
-      (*mapfun) (&taginfo, maparg);
+      mapfun (&taginfo, maparg);
       ADVANCE (p);
     }
     goto look_for_tag;
       ADVANCE (p);
     }
     goto look_for_tag;