home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Leser 15 / Amiga Plus Leser CD 15.iso / Tools / Development / MosaicSRC / libwww2 / HTParse.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-03-13  |  13.7 KB  |  511 lines

  1. /*        Parse HyperText Document Address        HTParse.c
  2. **        ================================
  3. */
  4.  
  5. #include "HTUtils.h"
  6. #include "HTParse.h"
  7. #include "tcp.h"
  8.  
  9. #define HEX_ESCAPE '%'
  10.  
  11. struct struct_parts {
  12.     char * access;
  13.     char * host;
  14.     char * absolute;
  15.     char * relative;
  16.     char * anchor;
  17. };
  18.  
  19.  
  20. /*    Strip white space off a string
  21. **    ------------------------------
  22. **
  23. ** On exit,
  24. **    Return value points to first non-white character, or to 0 if none.
  25. **    All trailing white space is OVERWRITTEN with zero.
  26. */
  27.  
  28. #ifdef __STDC__
  29. char * HTStrip(char * s)
  30. #else
  31. char * HTStrip(s)
  32.     char *s;
  33. #endif
  34. {
  35. #define SPACE(c) ((c==' ')||(c=='\t')||(c=='\n')) 
  36.     char * p=s;
  37.     for(p=s;*p;p++);                /* Find end of string */
  38.     for(p--;p>=s;p--) {
  39.         if(SPACE(*p)) *p=0;    /* Zap trailing blanks */
  40.     else break;
  41.     }
  42.     while(SPACE(*s))s++;    /* Strip leading blanks */
  43.     return s;
  44. }
  45.  
  46.  
  47. /*    Scan a filename for its consituents
  48. **    -----------------------------------
  49. **
  50. ** On entry,
  51. **    name    points to a document name which may be incomplete.
  52. ** On exit,
  53. **      absolute or relative may be nonzero (but not both).
  54. **    host, anchor and access may be nonzero if they were specified.
  55. **    Any which are nonzero point to zero terminated strings.
  56. */
  57. #ifdef __STDC__
  58. PRIVATE void scan(char * name, struct struct_parts *parts)
  59. #else
  60. PRIVATE void scan(name, parts)
  61.     char * name;
  62.     struct struct_parts *parts;
  63. #endif
  64. {
  65.     char * after_access;
  66.     char * p;
  67.     int length;
  68.  
  69.     if (name && *name)
  70.       length = strlen(name);
  71.     else
  72.       length = 0;
  73.     
  74.     parts->access = 0;
  75.     parts->host = 0;
  76.     parts->absolute = 0;
  77.     parts->relative = 0;
  78.     parts->anchor = 0;
  79.  
  80.     /* Argh. */
  81.     if (!length)
  82.       return;
  83.     
  84.     after_access = name;
  85.     for(p=name; *p; p++) {
  86.     if (*p==':') {
  87.         *p = 0;
  88.         parts->access = name;    /* Access name has been specified */
  89.         after_access = p+1;
  90.     }
  91.     if (*p=='/') break;
  92.     if (*p=='#') break;
  93.     }
  94.     
  95.     for(p=name+length-1; p>=name; p--) {
  96.     if (*p =='#') {
  97.         parts->anchor=p+1;
  98.         *p=0;                /* terminate the rest */
  99.     }
  100.     }
  101.     p = after_access;
  102.     if (*p=='/'){
  103.     if (p[1]=='/') {
  104.         parts->host = p+2;        /* host has been specified     */
  105.         *p=0;            /* Terminate access         */
  106.         p=strchr(parts->host,'/');    /* look for end of host name if any */
  107.         if(p) {
  108.             *p=0;            /* Terminate host */
  109.             parts->absolute = p+1;        /* Root has been found */
  110.         }
  111.     } else {
  112.         parts->absolute = p+1;        /* Root found but no host */
  113.     }        
  114.     } else {
  115.         parts->relative = (*after_access) ? after_access : 0;    /* zero for "" */
  116.     }
  117.  
  118.     /* Access specified but no host: the anchor was not really one
  119.        e.g. news:j462#36487@foo.bar -- JFG 10/7/92, from bug report */
  120.     if (parts->access && ! parts->host && parts->anchor) {
  121.       *(parts->anchor - 1) = '#';  /* Restore the '#' in the address */
  122.       parts->anchor = 0;
  123.     }
  124.  
  125. } /*scan */    
  126.  
  127.  
  128. /*    Parse a Name relative to another name
  129. **    -------------------------------------
  130. **
  131. **    This returns those parts of a name which are given (and requested)
  132. **    substituting bits from the related name where necessary.
  133. **
  134. ** On entry,
  135. **    aName        A filename given
  136. **      relatedName     A name relative to which aName is to be parsed
  137. **      wanted          A mask for the bits which are wanted.
  138. **
  139. ** On exit,
  140. **    returns        A pointer to a malloc'd string which MUST BE FREED
  141. */
  142. #ifdef __STDC__
  143. char * HTParse(char * aName, char * relatedName, int wanted)
  144. #else
  145. char * HTParse(aName, relatedName, wanted)
  146.     char * aName;
  147.     char * relatedName;
  148.     int wanted;
  149. #endif
  150.  
  151. {
  152.     char * result = 0;
  153.     char * return_value = 0;
  154.     int len;
  155.     char * name = 0;
  156.     char * rel = 0;
  157.     char * p;
  158.     char *access;
  159.     struct struct_parts given, related;
  160.     
  161.     if (!aName)
  162.       aName = strdup ("\0");
  163.     if (!relatedName)
  164.       relatedName = strdup ("\0");
  165.     
  166.     /* Make working copies of input strings to cut up:
  167.     */
  168.     len = strlen(aName)+strlen(relatedName)+10;
  169.     result=(char *)malloc(len);        /* Lots of space: more than enough */
  170.     
  171.     StrAllocCopy(name, aName);
  172.     StrAllocCopy(rel, relatedName);
  173.     
  174.     scan(name, &given);
  175.     scan(rel,  &related); 
  176.     result[0]=0;        /* Clear string  */
  177.     access = given.access ? given.access : related.access;
  178.     if (wanted & PARSE_ACCESS)
  179.         if (access) {
  180.         strcat(result, access);
  181.         if(wanted & PARSE_PUNCTUATION) strcat(result, ":");
  182.     }
  183.     
  184.     if (given.access && related.access)    /* If different, inherit nothing. */
  185.         if (strcmp(given.access, related.access)!=0) {
  186.         related.host=0;
  187.         related.absolute=0;
  188.         related.relative=0;
  189.         related.anchor=0;
  190.     }
  191.     
  192.     if (wanted & PARSE_HOST)
  193.         if(given.host || related.host) {
  194.           char * tail = result + strlen(result);   
  195.         if(wanted & PARSE_PUNCTUATION) strcat(result, "//");
  196.         strcat(result, given.host ? given.host : related.host);
  197. #define CLEAN_URLS
  198. #ifdef CLEAN_URLS
  199.         /* Ignore default port numbers, and trailing dots on FQDNs
  200.            which will only cause identical adreesses to look different */
  201.           {
  202.             char * p;
  203.             p = strchr(tail, ':');
  204.             if (p && access) 
  205.               {        /* Port specified */
  206.                 if ((strcmp(access, "http") == 0 && strcmp(p, ":80") == 0) ||
  207.                     (strcmp(access, "gopher") == 0 && 
  208.                      (strcmp(p, ":70") == 0 ||
  209.                       strcmp(p, ":70+") == 0)))
  210.                   *p = (char)0;    /* It is the default: ignore it */
  211.                 else if (p && *p && p[strlen(p)-1] == '+')
  212.                   p[strlen(p)-1] = 0;
  213.               }
  214.             if (!p) 
  215.               p = tail + strlen(tail); /* After hostname */
  216.             p--;                /* End of hostname */
  217.             if (strlen (tail) > 3 && (*p == '.')) 
  218.               {
  219.                 if (TRACE)
  220.                   fprintf (stderr, "[Parse] tail '%s' p '%s'\n", tail, p);
  221.                 *p = (char)0; /* chop final . */
  222.                 
  223.                 /* OK, at this point we know that *(p+1) exists,
  224.                    else we would not be here.
  225.  
  226.                    If it's 0, then we're done.
  227.  
  228.                    If it's not 0, then we move *(p+2) to *(p+1),
  229.                    etc.
  230.  
  231.                    Let's try to use a bcopy... */
  232.                 if (*(p+1) != '\0')
  233.                   {
  234.                     if (TRACE)
  235.                       fprintf (stderr, "[Parse] Copying '%s' to '%s', %d bytes\n", 
  236.                                p+1, p, strlen (p+1));
  237.                     bcopy (p+1, p, strlen(p+1));
  238.                     if (TRACE)
  239.                       fprintf (stderr, "[Parse] Setting '%c' to 0...\n",
  240.                                *(p + strlen (p+1)));
  241.                     *(p + strlen (p+1)) = '\0';
  242.                   }
  243.                 if (TRACE)
  244.                   fprintf (stderr, "[Parse] tail '%s' p '%s'\n", tail, p);
  245.               }
  246.             {
  247.               char *tmp;
  248.               tmp = strchr (tail, '@');
  249.               if (!tmp)
  250.                 tmp = tail;
  251.               for (; *tmp; tmp++)
  252.                 *tmp = TOLOWER (*tmp);
  253.             }
  254.           }
  255. #endif
  256.     }
  257.     
  258.     if (given.host && related.host)  /* If different hosts, inherit no path. */
  259.         if (strcmp(given.host, related.host)!=0) {
  260.         related.absolute=0;
  261.         related.relative=0;
  262.         related.anchor=0;
  263.     }
  264.     
  265.     if (wanted & PARSE_PATH) {
  266.         if(given.absolute) {                /* All is given */
  267.         if(wanted & PARSE_PUNCTUATION) strcat(result, "/");
  268.         strcat(result, given.absolute);
  269.     } else if(related.absolute) {    /* Adopt path not name */
  270.         strcat(result, "/");
  271.         strcat(result, related.absolute);
  272.         if (given.relative) {
  273.         p = strchr(result, '?');    /* Search part? */
  274.         if (!p) p=result+strlen(result)-1;
  275.         for (; *p!='/'; p--);    /* last / */
  276.         p[1]=0;                    /* Remove filename */
  277.         strcat(result, given.relative);        /* Add given one */
  278.         HTSimplify (result);
  279.         }
  280.     } else if(given.relative) {
  281.         strcat(result, given.relative);        /* what we've got */
  282.     } else if(related.relative) {
  283.         strcat(result, related.relative);
  284.     } else {  /* No inheritance */
  285.         strcat(result, "/");
  286.     }
  287.     }
  288.         
  289.     if (wanted & PARSE_ANCHOR)
  290.         if(given.anchor || related.anchor) {
  291.         if(wanted & PARSE_PUNCTUATION) strcat(result, "#");
  292.         strcat(result, given.anchor ? given.anchor : related.anchor);
  293.     }
  294.     if (rel)
  295.       free(rel);
  296.     if (name)
  297.       free(name);
  298.     
  299.     StrAllocCopy(return_value, result);
  300.     free(result);
  301.     return return_value;        /* exactly the right length */
  302. }
  303.  
  304.  
  305. /*            Simplify a filename
  306. //        -------------------
  307. //
  308. // A unix-style file is allowed to contain the seqeunce xxx/../ which may be
  309. // replaced by "" , and the seqeunce "/./" which may be replaced by "/".
  310. // Simplification helps us recognize duplicate filenames.
  311. //
  312. //    Thus,     /etc/junk/../fred     becomes    /etc/fred
  313. //        /etc/junk/./fred    becomes    /etc/junk/fred
  314. //
  315. //      but we should NOT change
  316. //        http://fred.xxx.edu/../..
  317. //
  318. //    or    ../../albert.html
  319. */
  320. #ifdef __STDC__
  321. void HTSimplify(char * filename)
  322. #else
  323. void HTSimplify(filename)
  324.     char * filename;
  325. #endif
  326.  
  327. {
  328.   char * p;
  329.   char * q;
  330.   if (filename[0] && filename[1])
  331.     {
  332.       for(p=filename+2; *p; p++) 
  333.         {
  334.           if (*p=='/') 
  335.             {
  336.               if ((p[1]=='.') && (p[2]=='.') && (p[3]=='/' || !p[3] )) 
  337.                 {
  338.                   /* Changed clause below to (q>filename) due to attempted
  339.                      read to q = filename-1 below. */
  340.                   for (q = p-1; (q>filename) && (*q!='/'); q--)
  341.                     ; /* prev slash */
  342.                   if (q[0]=='/' && 0!=strncmp(q, "/../", 4)
  343.                       && !(q-1>filename && q[-1]=='/')) 
  344.                     {
  345.                       strcpy(q, p+3);    /* Remove  /xxx/..    */
  346.                       if (!*filename) strcpy(filename, "/");
  347.                       p = q-1;        /* Start again with prev slash     */
  348.                     } 
  349.                 } 
  350.               else if ((p[1]=='.') && (p[2]=='/' || !p[2])) 
  351.                 {
  352.                   strcpy(p, p+2);            /* Remove a slash and a dot */
  353.                 }
  354.             }
  355.         }
  356.     }
  357. }
  358.   
  359.  
  360. /*        Make Relative Name
  361. **        ------------------
  362. **
  363. ** This function creates and returns a string which gives an expression of
  364. ** one address as related to another. Where there is no relation, an absolute
  365. ** address is retured.
  366. **
  367. **  On entry,
  368. **    Both names must be absolute, fully qualified names of nodes
  369. **    (no anchor bits)
  370. **
  371. **  On exit,
  372. **    The return result points to a newly allocated name which, if
  373. **    parsed by HTParse relative to relatedName, will yield aName.
  374. **    The caller is responsible for freeing the resulting name later.
  375. **
  376. */
  377. #ifdef __STDC__
  378. char * HTRelative(char * aName, char *relatedName)
  379. #else
  380. char * HTRelative(aName, relatedName)
  381.    char * aName;
  382.    char * relatedName;
  383. #endif
  384. {
  385.     char * result = 0;
  386.     CONST char *p = aName;
  387.     CONST char *q = relatedName;
  388.     CONST char * after_access = 0;
  389.     CONST char * path = 0;
  390.     CONST char * last_slash = 0;
  391.     int slashes = 0;
  392.     
  393.     for(;*p; p++, q++) {    /* Find extent of match */
  394.         if (*p!=*q) break;
  395.     if (*p==':') after_access = p+1;
  396.     if (*p=='/') {
  397.         last_slash = p;
  398.         slashes++;
  399.         if (slashes==3) path=p;
  400.     }
  401.     }
  402.     
  403.     /* q, p point to the first non-matching character or zero */
  404.     
  405.     if (!after_access) {            /* Different access */
  406.         StrAllocCopy(result, aName);
  407.     } else if (slashes<3){            /* Different nodes */
  408.         StrAllocCopy(result, after_access);
  409.     } else if (slashes==3){            /* Same node, different path */
  410.         StrAllocCopy(result, path);
  411.     } else {                    /* Some path in common */
  412.         int levels= 0;
  413.         for(; *q && (*q!='#'); q++)  if (*q=='/') levels++;
  414.     result = (char *)malloc(3*levels + strlen(last_slash) + 1);
  415.     result[0]=0;
  416.     for(;levels; levels--)strcat(result, "../");
  417.     strcat(result, last_slash+1);
  418.     }
  419.     if (TRACE) 
  420.       fprintf(stderr, "HT: `%s' expressed relative to\n    `%s' is\n   `%s'.",
  421.               aName, relatedName, result);
  422.     return result;
  423. }
  424.  
  425.  
  426. static unsigned char isAcceptable[96] =
  427. /*   0 1 2 3 4 5 6 7 8 9 A B C D E F */
  428. {    0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0,    /* 2x   !"#$%&'()*+,-./     */
  429.      1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,    /* 3x  0123456789:;<=>?     */
  430.      1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    /* 4x  @ABCDEFGHIJKLMNO  */
  431.      1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,    /* 5x  PQRSTUVWXYZ[\]^_     */
  432.      0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    /* 6x  `abcdefghijklmno     */
  433.      1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0 };    /* 7x  pqrstuvwxyz{\}~    DEL */
  434.  
  435. #define HT_HEX(i) (i < 10 ? '0'+i : 'A'+ i - 10)
  436.  
  437. /* The string returned from here, if any, can be free'd by caller. */
  438. char *HTEscape (char *part)
  439. {
  440.   char *q, *u;
  441.   char *p, *s, *e;        /* Pointers into keywords */
  442.   char *escaped;
  443.  
  444.   if (!part)
  445.     return NULL;
  446.  
  447.   escaped = (char *)malloc (strlen (part) * 3 + 1);
  448.   
  449.   for (q = escaped, p = part; *p != '\0'; p++)
  450.     {
  451.       int c = (int)((unsigned char)(*p));
  452.       if (c >= 32 && c <= 127 && isAcceptable[c-32])
  453.         {
  454.           *q++ = *p;
  455.         }
  456.       else
  457.         {
  458.           *q++ = '%';
  459.           *q++ = HT_HEX(c / 16);
  460.           *q++ = HT_HEX(c % 16);
  461.         }
  462.     }
  463.   
  464.   *q=0;
  465.   
  466.   return escaped;
  467. }
  468.  
  469.  
  470.  
  471.  
  472. /*        Decode %xx escaped characters            HTUnEscape()
  473. **        -----------------------------
  474. **
  475. **    This function takes a pointer to a string in which some
  476. **    characters may have been encoded in %xy form, where xy is
  477. **    the acsii hex code for character 16x+y.
  478. **    The string is converted in place, as it will never grow.
  479. */
  480.  
  481. PRIVATE char from_hex ARGS1(char, c)
  482. {
  483.     return  c >= '0' && c <= '9' ?  c - '0' 
  484.             : c >= 'A' && c <= 'F'? c - 'A' + 10
  485.             : c - 'a' + 10;    /* accept small letters just in case */
  486. }
  487.  
  488. PUBLIC char * HTUnEscape ARGS1( char *, str)
  489. {
  490.     char * p = str;
  491.     char * q = str;
  492.     while(*p) {
  493.         if (*p == HEX_ESCAPE) {
  494.         p++;
  495.         if (*p) *q = from_hex(*p++) * 16;
  496.         if (*p) *q = (*q + from_hex(*p++));
  497.         q++;
  498.         } else if (*p == '+')
  499.           {
  500.             p++;
  501.             *q++ = ' ';
  502.     } else {
  503.         *q++ = *p++; 
  504.     }
  505.     }
  506.     
  507.     *q++ = 0;
  508.     return str;
  509.     
  510. } /* HTUnEscape */
  511.