URI:
       grabtitle.c - grabtitle - stupid HTML title grabber
  HTML git clone git://git.codemadness.org/grabtitle
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       grabtitle.c (4428B)
       ---
            1 #include <errno.h>
            2 #include <stdio.h>
            3 #include <stdlib.h>
            4 #include <string.h>
            5 #include <strings.h>
            6 
            7 #include "xml.h"
            8 
            9 #ifdef __OpenBSD__
           10 #include <unistd.h>
           11 #else
           12 #define pledge(a,b) 0
           13 #endif
           14 
           15 /* ctype-like macros, but always compatible with ASCII / UTF-8 */
           16 #define ISCNTRL(c) ((c) < ' ' || (c) == 0x7f)
           17 #define TOLOWER(c) ((((unsigned)c) - 'A' < 26) ? ((c) | 32) : (c))
           18 
           19 static XMLParser parser;
           20 
           21 static int ignore = 1;
           22 
           23 static char *ignorestate;
           24 static char endtagmatch[16];
           25 static unsigned char pushbuf[32], *pushoff, *pushend; /* pushback buffer */
           26 static unsigned char *pushtagend; /* end position of tag in pushback buffer */
           27 static int (*getnext)(void);
           28 
           29 static void
           30 getnext_literal_reset(void)
           31 {
           32         pushoff = pushbuf;
           33         pushend = pushbuf;
           34         pushtagend = NULL;
           35 }
           36 
           37 static void
           38 getnext_literal_end(void)
           39 {
           40         parser.getnext = getnext; /* restore original getnext callback */
           41 }
           42 
           43 /* Collect data as literal text, used for certain HTML tags such as <script> or
           44    <title> until some case-insensitive string occurs (closing tag).
           45    This code is very ugly and I'm not proud of it. Mmmmm spaghetti. */
           46 static int
           47 getnext_literal(void)
           48 {
           49         char *p;
           50         int c;
           51 
           52         /* check if there is data in the pushback buffer: process it first. */
           53         if (pushend != pushbuf) {
           54                 if (pushoff >= pushend) {
           55                         /* reached the offset where the tag ended */
           56                         if (pushtagend) {
           57                                 c = *pushtagend;
           58                                 getnext_literal_end();
           59                                 return c; /* end tag */
           60                         }
           61                         /* otherwise reached the end of pushback buffer, proceed normally */
           62                         getnext_literal_reset();
           63                 } else {
           64                         return (int)*(pushoff++);
           65                 }
           66         }
           67 
           68         if (*ignorestate) {
           69                 for (; *ignorestate; ignorestate++) {
           70                         if ((c = getnext()) == EOF)
           71                                 return EOF;
           72                         if (TOLOWER((unsigned char)c) != TOLOWER((unsigned char)*ignorestate))
           73                                 break;
           74                 }
           75         } else {
           76                 c = EOF; /* cannot happen, endtagmatch should be set (not empty) */
           77         }
           78 
           79         /* typical case: regular character */
           80         if (ignorestate == endtagmatch)
           81                 return c;
           82 
           83         /* process a partial or full match on the tag:
           84            full: handle as end tag. partial: handle "<" as a data entity "&lt;".
           85            buffers are not checked, they always fit (size of tag + space for at least "&lt;&lt;"). */
           86         pushoff = pushbuf;
           87         pushend = pushbuf;
           88         if (*ignorestate == '\0') { /* full match */
           89                 for (p = endtagmatch; *p && p < ignorestate; p++)
           90                         *(pushend++) = *p;
           91                 pushtagend = pushend; /* offset where the tag ended */
           92         } else { /* partial match */
           93                 for (p = endtagmatch; *p && p < ignorestate; p++) {
           94                         if (*p == '<') {
           95                                 memcpy(pushend, "&lt;", 4);
           96                                 pushend += 4;
           97                         } else {
           98                                 *(pushend++) = *p;
           99                         }
          100                 }
          101                 /* append current character to end of pushback buffer */
          102                 if (c == '<') {
          103                         memcpy(pushend, "&lt;", 4);
          104                         pushend += 4;
          105                 } else {
          106                         *(pushend++) = c;
          107                 }
          108         }
          109         ignorestate = endtagmatch; /* reset state for matching to beginning */
          110 
          111         return (int)*(pushoff++); /* first character in pushback buffer, always exists */
          112 }
          113 
          114 static void
          115 getnext_literal_start(const char *t)
          116 {
          117         snprintf(endtagmatch, sizeof(endtagmatch), "</%s>", t);
          118         ignorestate = endtagmatch;
          119         getnext_literal_reset(); /* reset pushback buffer */
          120 
          121         getnext = parser.getnext; /* for restore */
          122         parser.getnext = getnext_literal;
          123 }
          124 
          125 static void
          126 xmltagend(XMLParser *p, const char *t, size_t tl, int isshort)
          127 {
          128         if (ignore)
          129                 return;
          130 
          131         putchar('\n');
          132         exit(0);
          133 }
          134 
          135 /* data and CDATA */
          136 static void
          137 xmldata(XMLParser *p, const char *d, size_t dl)
          138 {
          139         size_t i;
          140 
          141         if (ignore)
          142                 return;
          143 
          144         for (i = 0; i < dl; i++, d++) {
          145                 if (!ISCNTRL((unsigned char)*d))
          146                         putchar(*d);
          147         }
          148 }
          149 
          150 static void
          151 xmldataentity(XMLParser *p, const char *d, size_t dl)
          152 {
          153         char buf[16];
          154         ssize_t len;
          155 
          156         if ((len = xml_entitytostr(d, buf, sizeof(buf))) > 0)
          157                 xmldata(p, buf, (size_t)len);
          158         else
          159                 xmldata(p, d, dl);
          160 }
          161 
          162 static void
          163 xmltagstartparsed(XMLParser *p, const char *t, size_t tl, int isshort)
          164 {
          165         if (!strcasecmp(t, "title")) {
          166                 ignore = 0;
          167         } else if (!strcasecmp(t, "script") ||
          168                    !strcasecmp(t, "style") ||
          169                    !strcasecmp(t, "template") ||
          170                    !strcasecmp(t, "textarea")) {
          171         } else {
          172                 return;
          173         }
          174 
          175         getnext_literal_start(t);
          176 }
          177 
          178 int
          179 main(void)
          180 {
          181         if (pledge("stdio", NULL) == -1) {
          182                 fprintf(stderr, "pledge: %s\n", strerror(errno));
          183                 return 2;
          184         }
          185 
          186         parser.xmlcdata = xmldata;
          187         parser.xmldata = xmldata;
          188         parser.xmldataentity = xmldataentity;
          189         parser.xmltagend = xmltagend;
          190         parser.xmltagstartparsed = xmltagstartparsed;
          191         parser.getnext = getchar;
          192         xml_parse(&parser);
          193 
          194         return 1;
          195 }