Diff for /Amaya/amaya/AHTURLTools.c between versions 1.24 and 1.25

version 1.24, 1997/02/18 15:04:55 version 1.25, 1997/03/18 12:06:51
Line 7 Line 7
   
 /*  /*
  * AHTURLTools.c: contains all the functions for testing, manipulating,   * AHTURLTools.c: contains all the functions for testing, manipulating,
  * and normalizing URLs.   * and normalizing URLs. It also contains a local copy of the libWWW
    * URL parsing functions.
  *   *
  * Authors: J. Kahan, I. Vatton   * Authors: J. Kahan, I. Vatton
  *   *
Line 174  char               *path; Line 175  char               *path;
    i = 0;     i = 0;
    while (suffix[i] != EOS)     while (suffix[i] != EOS)
      {       {
        nsuffix[i] = TOLOWER (suffix[i]);         nsuffix[i] = tolower (suffix[i]);
        i++;         i++;
      }       }
    nsuffix[i] = EOS;     nsuffix[i] = EOS;
Line 190  char               *path; Line 191  char               *path;
        i = 0;         i = 0;
        while (suffix[i] != EOS)         while (suffix[i] != EOS)
          {           {
            nsuffix[i] = TOLOWER (suffix[i]);             nsuffix[i] = tolower (suffix[i]);
            i++;             i++;
          }           }
        nsuffix[i] = EOS;         nsuffix[i] = EOS;
Line 231  char               *path; Line 232  char               *path;
    i = 0;     i = 0;
    while (suffix[i] != EOS)     while (suffix[i] != EOS)
      {       {
        nsuffix[i] = TOLOWER (suffix[i]);         nsuffix[i] = tolower (suffix[i]);
        i++;         i++;
      }       }
    nsuffix[i] = EOS;     nsuffix[i] = EOS;
Line 268  char               *path; Line 269  char               *path;
    i = 0;     i = 0;
    while (suffix[i] != EOS)     while (suffix[i] != EOS)
      {       {
         nsuffix[i] = TOLOWER (suffix[i]);          nsuffix[i] = tolower (suffix[i]);
         i++;          i++;
      }       }
    nsuffix[i] = EOS;     nsuffix[i] = EOS;
Line 288  char               *path; Line 289  char               *path;
        i = 0;         i = 0;
        while (suffix[i] != EOS)         while (suffix[i] != EOS)
          {           {
            nsuffix[i] = TOLOWER (suffix[i]);             nsuffix[i] = tolower (suffix[i]);
            i++;             i++;
          }           }
        nsuffix[i] = EOS;         nsuffix[i] = EOS;
Line 452  char               *docName; Line 453  char               *docName;
        /* the name is complete, go to the Sixth Step */         /* the name is complete, go to the Sixth Step */
        strcpy (newName, tempOrgName);         strcpy (newName, tempOrgName);
        /* verify if the URL has the form "protocol://server:port" */         /* verify if the URL has the form "protocol://server:port" */
        ptr = HTParse (newName, "", PARSE_ACCESS | PARSE_HOST |         ptr = AmayaParseUrl (newName, "", AMAYA_PARSE_ACCESS | AMAYA_PARSE_HOST |
                       PARSE_PUNCTUATION);                        AMAYA_PARSE_PUNCTUATION);
        if (ptr && !strcmp (ptr, newName))         if (ptr && !strcmp (ptr, newName))
          {           {
            /* it has this form, we complete it by adding a "/"  */             /* it has this form, we complete it by adding a "/"  */
            strcat (newName, "/");             strcat (newName, "/");
          }           }
        if (ptr)         if (ptr)
          HT_FREE (ptr);           TtaFreeMemory (ptr);
      }       }
    else if ( doc == 0)     else if ( doc == 0)
      /* the name is complete, go to the Sixth Step */       /* the name is complete, go to the Sixth Step */
Line 494  char               *docName; Line 495  char               *docName;
                if (basename[0] != EOS && basename[length] != '/')                  if (basename[0] != EOS && basename[length] != '/') 
                  /* verify if the base has the form "protocol://server:port" */                   /* verify if the base has the form "protocol://server:port" */
                  {                   {
                    ptr = HTParse (basename, "", PARSE_ACCESS | PARSE_HOST |                     ptr = AmayaParseUrl (basename, "", AMAYA_PARSE_ACCESS | AMAYA_PARSE_HOST |
                                                 PARSE_PUNCTUATION);                                                  AMAYA_PARSE_PUNCTUATION);
                    if (ptr && !strcmp (ptr, basename))                     if (ptr && !strcmp (ptr, basename))
                      {                       {
                      /* it has this form, we complete it by adding a "/"  */                       /* it has this form, we complete it by adding a "/"  */
Line 503  char               *docName; Line 504  char               *docName;
                      length++;                       length++;
                      }                       }
                    if (ptr)                     if (ptr)
                      HT_FREE (ptr);                       TtaFreeMemory (ptr);
                  }                   }
                /* Third Step: prepare the base                 /* Third Step: prepare the base
                ** Removing anything after the                 ** Removing anything after the
Line 576  char               *docName; Line 577  char               *docName;
        ** Fifth Step, calculate the absolute URL, using the base         ** Fifth Step, calculate the absolute URL, using the base
        */         */
   
        ptr = HTParse (tempOrgName, basename, PARSE_ALL);         ptr = AmayaParseUrl (tempOrgName, basename, AMAYA_PARSE_ALL);
   
        if (ptr)         if (ptr)
          {           {
            ptr = HTSimplify (&ptr);             ptr = AmayaSimplifyUrl (&ptr);
            strcpy (newName, ptr);             strcpy (newName, ptr);
            HT_FREE (ptr);             TtaFreeMemory (ptr);
          }           }
        else         else
            newName[0] = EOS;             newName[0] = EOS;
Line 635  char               *path; Line 636  char               *path;
    char               *basename_ptr1, *basename_ptr2;     char               *basename_ptr1, *basename_ptr2;
    boolean             result;     boolean             result;
   
    basename_ptr1 = HTParse (url1, "", PARSE_ACCESS | PARSE_HOST | PARSE_PUNCTUATION);     basename_ptr1 = AmayaParseUrl (url1, "", AMAYA_PARSE_ACCESS | AMAYA_PARSE_HOST | AMAYA_PARSE_PUNCTUATION);
    basename_ptr2 = HTParse (url2, "", PARSE_ACCESS | PARSE_HOST | PARSE_PUNCTUATION);     basename_ptr2 = AmayaParseUrl (url2, "", AMAYA_PARSE_ACCESS | AMAYA_PARSE_HOST | AMAYA_PARSE_PUNCTUATION);
   
    if (strcmp (basename_ptr1, basename_ptr2))     if (strcmp (basename_ptr1, basename_ptr2))
       result = FALSE;        result = FALSE;
    else     else
       result = TRUE;        result = TRUE;
   
    HT_FREE (basename_ptr1);     TtaFreeMemory (basename_ptr1);
    HT_FREE (basename_ptr2);     TtaFreeMemory (basename_ptr2);
   
    return (result);     return (result);
 }  }
Line 670  char                base_url; Line 671  char                base_url;
   
    /* verify if we are in the same host */     /* verify if we are in the same host */
   
    base_ptr = HTParse (base_url, "", PARSE_ACCESS | PARSE_HOST | PARSE_PUNCTUATION);     base_ptr = AmayaParseUrl (base_url, "", AMAYA_PARSE_ACCESS | AMAYA_PARSE_HOST | AMAYA_PARSE_PUNCTUATION);
    url_ptr = HTParse (url, "", PARSE_ACCESS | PARSE_HOST | PARSE_PUNCTUATION);     url_ptr = AmayaParseUrl (url, "", AMAYA_PARSE_ACCESS | AMAYA_PARSE_HOST | AMAYA_PARSE_PUNCTUATION);
   
    if (!strcmp (base_ptr, url_ptr))     if (!strcmp (base_ptr, url_ptr))
      {       {
         HT_FREE (base_ptr);          TtaFreeMemory (base_ptr);
         HT_FREE (url_ptr);          TtaFreeMemory (url_ptr);
   
         /* Normalize the URLs */          /* Normalize the URLs */
   
         base_ptr = HTParse (base_url, "", PARSE_ALL);          base_ptr = AmayaParseUrl (base_url, "", AMAYA_PARSE_ALL);
         url_ptr = HTParse (url, "", PARSE_ALL);          url_ptr = AmayaParseUrl (url, "", AMAYA_PARSE_ALL);
   
         /* Use libwww to make relative name */          /* Use libwww to make relative name */
   
         result = HTRelative (url_ptr, base_ptr);          result = AmayaRelativeUrl (url_ptr, base_ptr);
         HT_FREE (base_ptr);          TtaFreeMemory (base_ptr);
         HT_FREE (url_ptr);          TtaFreeMemory (url_ptr);
      }       }
    else     else
       result = (char *) NULL;        result = (char *) NULL;
Line 712  char               *path; Line 713  char               *path;
    if (!path || path[0] == EOS || path[strlen(path)] == DIR_SEP)     if (!path || path[0] == EOS || path[strlen(path)] == DIR_SEP)
      return (FALSE);       return (FALSE);
   
    root = HTParse(path, (char *) NULL, PARSE_PATH | PARSE_PUNCTUATION);     root = AmayaParseUrl(path, (char *) NULL, AMAYA_PARSE_PATH | AMAYA_PARSE_PUNCTUATION);
   
    if (root)      if (root) 
      {       {
        strcpy (temppath, root);         strcpy (temppath, root);
        HT_FREE (root);         TtaFreeMemory (root);
        /* Get the suffix */         /* Get the suffix */
        ExtractSuffix (temppath, suffix);          ExtractSuffix (temppath, suffix); 
   
Line 775  char                *string; Line 776  char                *string;
    return;     return;
   
  for (i = 0; string[i] != EOS; i++)   for (i = 0; string[i] != EOS; i++)
    string[i] = TOLOWER (string[i]);     string[i] = tolower (string[i]);
 }  }
   
   
Line 816  char *inputURL; Line 817  char *inputURL;
     }      }
 }  }
   
   /************************************************************************
    *                                                                      *
    *      Local Adaptation of the libWWW Library/src/AmayaParseUrl.c code.        *
    *                                                                      *
    ************************************************************************/
   
   #define StringAllocCopy(dest,src) {                                     \
       if (src == NULL) dest = NULL;                                       \
       else {                                                              \
           if ((dest = (char *) TtaGetMemory(strlen(src) + 1)) == NULL)    \
                   exit(1);                                                \
           else strcpy(dest, src); }}                                      \
   
   typedef struct _HTURI {
       char * access;              /* Now known as "scheme" */
       char * host;
       char * absolute;
       char * relative;
       char * fragment;
   } HTURI;
   
   /*----------------------------------------------------------------------
      scan
           Scan a filename for its consituents
           -----------------------------------
     
      On entry,
           name    points to a document name which may be incomplete.
      On exit,
           absolute or relative may be nonzero (but not both).
           host, fragment and access may be nonzero if they were specified.
           Any which are nonzero point to zero terminated strings.
     ----------------------------------------------------------------------*/
   #ifdef __STDC__
   static void scan (char * name, HTURI * parts)
   #else  /* __STDC__ */
   static void scan (name, parts)
   char                *name;
   HTURI               *parts;
   
   #endif /* __STDC__ */
   {
       char * p;
       char * after_access = name;
       memset(parts, '\0', sizeof(HTURI));
   
       /* Look for fragment identifier */
       if ((p = strrchr(name, '#')) != NULL) {
           *p++ = '\0';
           parts->fragment = p;
       }
       
       for(p=name; *p; p++) {
           if (*p=='/' || *p=='#' || *p=='?')
               break;
           if (*p==':') {
                   *p = 0;
                   parts->access = after_access; /* Scheme has been specified */
   
   /* The combination of gcc, the "-O" flag and the HP platform is
      unhealthy. The following three lines is a quick & dirty fix, but is
      not recommended. Rather, turn off "-O". */
   
   /*              after_access = p;*/
   /*              while (*after_access == 0)*/
   /*                  after_access++;*/
   
                   after_access = p+1;
   
                   if (0==strcasecmp("URL", parts->access)) {
                       parts->access = NULL;  /* Ignore IETF's URL: pre-prefix */
                   } else break;
           }
       }
       
       p = after_access;
       if (*p=='/'){
           if (p[1]=='/') {
               parts->host = p+2;          /* host has been specified      */
               *p=0;                       /* Terminate access             */
               p=strchr(parts->host,'/');  /* look for end of host name if any */
               if(p) {
                   *p=0;                   /* Terminate host */
                   parts->absolute = p+1;          /* Root has been found */
               }
           } else {
               parts->absolute = p+1;              /* Root found but no host */
           }           
       } else {
           parts->relative = (*after_access) ? after_access : 0; /* zero for "" */
       }
   }
   
   
   /*----------------------------------------------------------------------
       AmayaParseUrl
           Parse a Name relative to another name
           -------------------------------------
     
           This returns those parts of a name which are given (and requested)
           substituting bits from the related name where necessary.
     
      On entry,
           aName           A filename given
           relatedName     A name relative to which aName is to be parsed. Give
                           it an empty string if aName is absolute.
           wanted          A mask for the bits which are wanted.
     
      On exit,
           returns         A pointer to a malloc'd string which MUST BE FREED
     
     ----------------------------------------------------------------------*/
   #ifdef __STDC__
   char * AmayaParseUrl (const char *aName, const char *relatedName, int wanted)
   #else  /* __STDC__ */
   char * AmayaParseUrl (aName, relatedName, wanted)
   const char          *aName;
   const char          *relatedName;
   int                  wanted;
   
   #endif /* __STDC__ */
   {
       char * result = 0;
       char * return_value = 0;
       int len;
       char * name = 0;
       char * rel = 0;
       char * p;
       char * access;
       HTURI given, related;
       
       if (!relatedName)        /* HWL 23/8/94: dont dump due to NULL */
           relatedName = "";
       
       /* Make working copies of input strings to cut up: */
       len = strlen(aName)+strlen(relatedName)+10;
       if ((result=(char *) TtaGetMemory(len)) == NULL) /* Lots of space: more than enough */
           exit(1);
       StringAllocCopy(name, aName);
       StringAllocCopy(rel, relatedName);
       
       scan(name, &given);
       scan(rel,  &related); 
       result[0]=0;                /* Clear string  */
       access = given.access ? given.access : related.access;
       if (wanted & AMAYA_PARSE_ACCESS)
           if (access) {
               strcat(result, access);
               if(wanted & AMAYA_PARSE_PUNCTUATION) strcat(result, ":");
           }
           
       if (given.access && related.access) /* If different, inherit nothing. */
           if (strcmp(given.access, related.access)!=0) {
               related.host=0;
               related.absolute=0;
               related.relative=0;
               related.fragment=0;
           }
           
       if (wanted & AMAYA_PARSE_HOST)
           if(given.host || related.host) {
               if(wanted & AMAYA_PARSE_PUNCTUATION) strcat(result, "//");
               strcat(result, given.host ? given.host : related.host);
           }
           
       if (given.host && related.host)  /* If different hosts, inherit no path. */
           if (strcmp(given.host, related.host)!=0) {
               related.absolute=0;
               related.relative=0;
               related.fragment=0;
           }
           
       if (wanted & AMAYA_PARSE_PATH) {
           if(given.absolute) {                            /* All is given */
               if(wanted & AMAYA_PARSE_PUNCTUATION) strcat(result, "/");
               strcat(result, given.absolute);
           } else if(related.absolute) {   /* Adopt path not name */
               strcat(result, "/");
               strcat(result, related.absolute);
               if (given.relative) {
                   p = strchr(result, '?');        /* Search part? */
                   if (!p) p=result+strlen(result)-1;
                   for (; *p!='/'; p--);   /* last / */
                   p[1]=0;                                 /* Remove filename */
                   strcat(result, given.relative);         /* Add given one */
   #if 0
                   result = AmayaSimplifyUrl (&result);
   #endif
               }
           } else if(given.relative) {
               strcat(result, given.relative);             /* what we've got */
           } else if(related.relative) {
               strcat(result, related.relative);
           } else {  /* No inheritance */
               strcat(result, "/");
           }
       }
                   
       if (wanted & AMAYA_PARSE_ANCHOR)
           if(given.fragment || related.fragment) {
               if(given.absolute && given.fragment) {   /*Fixes for relURLs...*/
                   if(wanted & AMAYA_PARSE_PUNCTUATION) strcat(result, "#");
                   strcat(result, given.fragment); 
               } else if (!(given.absolute) && !(given.fragment)) {
                   strcat(result, "");
               } else {
                   if(wanted & AMAYA_PARSE_PUNCTUATION) strcat(result, "#");
                   strcat(result, given.fragment ? given.fragment : related.fragment); 
               }
           }
       TtaFreeMemory(rel);
       TtaFreeMemory(name);
       
       StringAllocCopy(return_value, result);
       TtaFreeMemory(result);
       return return_value;                /* exactly the right length */
   }
   
   /*----------------------------------------------------------------------
        HTCanon
           Canonicalizes the URL in the following manner starting from the host
           pointer:
     
           1) The host name is converted to lowercase
           2) Chop off port if `:80' (http), `:70' (gopher), or `:21' (ftp)
     
           Return: OK      The position of the current path part of the URL
                           which might be the old one or a new one.
     
     ----------------------------------------------------------------------*/
   #ifdef __STDC__
   static char * HTCanon (char ** filename, char * host)
   #else  /* __STDC__ */
   static char * HTCanon (filename,host)
   char               **filename;
   char                *host;
   #endif /* __STDC__ */
   {
       char *newname = NULL;
       char *port;
       char *strptr;
       char *path;
       char *access = host-3;
   
       while (access>*filename && *(access-1)!='/')       /* Find access method */
           access--;
       if ((path = strchr(host, '/')) == NULL)                     /* Find path */
           path = host + strlen(host);
       if ((strptr = strchr(host, '@')) != NULL && strptr<path)       /* UserId */
           host = strptr;
       if ((port = strchr(host, ':')) != NULL && port>path)      /* Port number */
           port = NULL;
   
       strptr = host;                                  /* Convert to lower-case */
       while (strptr<path) {
           *strptr = tolower(*strptr);
           strptr++;
       }
       
       /* Does the URL contain a full domain name? This also works for a
          numerical host name. The domain name is already made lower-case
          and without a trailing dot. */
       {
           char *dot = port ? port : path;
           if (dot > *filename && *--dot=='.') {
               char *orig=dot, *dest=dot+1;
               while((*orig++ = *dest++));
               if (port) port--;
               path--;
           }
       }
       /* Chop off port if `:', `:80' (http), `:70' (gopher), or `:21' (ftp) */
       if (port) {
           if (!*(port+1) || *(port+1)=='/') {
               if (!newname) {
                   char *orig=port, *dest=port+1;
                   while((*orig++ = *dest++));
               }
           } else if ((!strncmp(access, "http", 4) &&
                (*(port+1)=='8'&&*(port+2)=='0'&&(*(port+3)=='/'||!*(port+3)))) ||
               (!strncmp(access, "gopher", 6) &&
                (*(port+1)=='7'&&*(port+2)=='0'&&(*(port+3)=='/'||!*(port+3)))) ||
               (!strncmp(access, "ftp", 3) &&
                (*(port+1)=='2'&&*(port+2)=='1'&&(*(port+3)=='/'||!*(port+3))))) {
               if (!newname) {
                   char *orig=port, *dest=port+3;
                   while((*orig++ = *dest++));
                   path -= 3;             /* Update path position, Henry Minsky */
               }
           } else if (newname)
               strncat(newname, port, (int) (path-port));
       }
   
       if (newname) {
           char *newpath = newname+strlen(newname);
           strcat(newname, path);
           path = newpath;
           TtaFreeMemory(*filename);                                   /* Free old copy */
           *filename = newname;
       }
       return path;
   }
   
   
   /*----------------------------------------------------------------------
       AmayaSimplifyUrl
                   Simplify a URI
                   --------------
      A URI is allowed to contain the seqeunce xxx/../ which may be
      replaced by "" , and the seqeunce "/./" which may be replaced by "/".
      Simplification helps us recognize duplicate URIs. 
     
           Thus,   /etc/junk/../fred       becomes /etc/fred
                   /etc/junk/./fred        becomes /etc/junk/fred
     
           but we should NOT change
                   http://fred.xxx.edu/../..
     
           or      ../../albert.html
     
      In order to avoid empty URLs the following URLs become:
     
                   /fred/..                becomes /fred/..
                   /fred/././..            becomes /fred/..
                   /fred/.././junk/.././   becomes /fred/..
     
      If more than one set of `://' is found (several proxies in cascade) then
      only the part after the last `://' is simplified.
     
      Returns: A string which might be the old one or a new one.
     
     ----------------------------------------------------------------------*/
   #ifdef __STDC__
   char *AmayaSimplifyUrl (char ** url)
   #else  /* __STDC__ */
   char *AmayaSimplifyUrl (url)
   char               **url;
   #endif /* __STDC__ */
   {
       char *path;
       char *p;
       if (!url || !*url) {
           return *url;
       }
   
       /* Find any scheme name */
       if ((path = strstr(*url, "://")) != NULL) {            /* Find host name */
           char *newptr;
           char *access = *url;
           while (access<path && (*access=tolower(*access))) access++;
           path += 3;
           while ((newptr = strstr(path, "://")) != NULL)        /* For proxies */
               path = newptr+3;
           path = HTCanon(url, path);                    /* We have a host name */
       } else if ((path = strstr(*url, ":/")) != NULL) {
           path += 2;
       } else
           path = *url;
       if (*path == '/' && *(path+1)=='/') {         /* Some URLs start //<foo> */
           path += 1;
       } else if (!strncmp(path, "news:", 5)) {
           char *ptr = strchr(path+5, '@');
           if (!ptr) ptr = path+5;
           while (*ptr) {                      /* Make group or host lower case */
               *ptr = tolower(*ptr);
               ptr++;
           }
           return *url;                  /* Doesn't need to do any more */
       }
       if ((p = path)) {
           char *end;
           if (!((end = strchr(path, ';')) || (end = strchr(path, '?')) ||
                 (end = strchr(path, '#'))))
               end = path+strlen(path);
   
           /* Parse string second time to simplify */
           p = path;
           while(p<end) {
               if (*p=='/') {
                   if (p>*url && *(p+1)=='.' && (*(p+2)=='/' || !*(p+2))) {
                       char *orig = p+1;
                       char *dest = (*(p+2)!='/') ? p+2 : p+3;
                       while ((*orig++ = *dest++)); /* Remove a slash and a dot */
                       end = orig-1;
                   } else if (*(p+1)=='.' && *(p+2)=='.' && (*(p+3)=='/' || !*(p+3))) {
                       char *q = p;
                       while (q>path && *--q!='/');               /* prev slash */
                       if (strncmp(q, "/../", 4)) {
                           char *orig = q+1;
                           char *dest = (*(p+3)!='/') ? p+3 : p+4;
                           while ((*orig++ = *dest++));       /* Remove /xxx/.. */
                           end = orig-1;
                           p = q;                /* Start again with prev slash */
                       } else
                           p++;
                   } else if (*(p+1)=='/') {
                       while (*(p+1)=='/') {
                           char *orig=p, *dest=p+1;
                           while ((*orig++ = *dest++));  /* Remove multiple /'s */
                           end = orig-1;
                       }
                   } else
                       p++;
               } else
                   p++;
           }
       }
       return *url;
   }
   
   /*----------------------------------------------------------------------
        AmayaRelativeUrl
                   Make Relative Name
                   ------------------
     
      This function creates and returns a string which gives an expression of
      one address as related to another. Where there is no relation, an absolute
      address is retured.
     
       On entry,
           Both names must be absolute, fully qualified names of nodes
           (no fragment bits)
     
       On exit,
           The return result points to a newly allocated name which, if
           parsed by AmayaParseUrl relative to relatedName, will yield aName.
           The caller is responsible for freeing the resulting name later.
     
     ----------------------------------------------------------------------*/
   #ifdef __STDC__
   char * AmayaRelativeUrl (const char * aName, const char * relatedName)
   #else  /* __STDC__ */
   char * AmayaRelativeUrl (const char * aName, const char * relatedName)
   const char          *aName;
   #endif  /* __STDC__ */
   {
       char * result = 0;
       const char *p = aName;
       const char *q = relatedName;
       const char * after_access = 0;
       const char * path = 0;
       const char * last_slash = 0;
       int slashes = 0;
       
       for(;*p; p++, q++) {        /* Find extent of match */
           if (*p!=*q) break;
           if (*p==':') after_access = p+1;
           if (*p=='/') {
               last_slash = p;
               slashes++;
               if (slashes==3) path=p;
           }
       }
       
       /* q, p point to the first non-matching character or zero */
       
       if (!after_access) {                        /* Different access */
           StringAllocCopy(result, aName);
       } else if (slashes<3){                      /* Different nodes */
           StringAllocCopy(result, after_access);
       } else {                                    /* Some path in common */
           int levels= 0;
           for(; *q && (*q!='#'); q++)  if (*q=='/') levels++;
           if ((result = (char  *) TtaGetMemory(3*levels + strlen(last_slash) + 1)) == NULL)
               exit(1);
           result[0]=0;
           for(;levels; levels--)strcat(result, "../");
           strcat(result, last_slash+1);
       }
       return result;
   }
   
   
 /*  /*
   end of Module AHTURLTools.c    end of Module AHTURLTools.c

Removed from v.1.24  
changed lines
  Added in v.1.25


Webmaster