Return to AHTURLTools.c CVS log | Up to [Public] / Amaya / amaya |
1.7 cvs 1: /* 2: * 3: * (c) COPYRIGHT MIT and INRIA, 1996. 4: * Please first read the full copyright statement in file COPYRIGHT. 5: * 6: */ 1.9 cvs 7: 1.10 cvs 8: /* 9: * AHTURLTools.c: contains all the functions for testing, manipulating, 1.25 cvs 10: * and normalizing URLs. It also contains a local copy of the libWWW 11: * URL parsing functions. 1.10 cvs 12: * 13: * Authors: J. Kahan, I. Vatton 14: * 15: */ 1.7 cvs 16: 1.8 cvs 17: /* Amaya includes */ 1.15 cvs 18: #define THOT_EXPORT extern 1.3 cvs 19: #include "amaya.h" 20: 1.8 cvs 21: #include "init_f.h" 22: #include "AHTURLTools_f.h" 23: 1.24 cvs 24: /* Local definitions */ 25: 26: #define MAX_PRINT_URL_LENGTH 50 27: 1.22 cvs 28: /* Private functions */ 29: #ifdef __STDC__ 30: static void ConvertToLowerCase (char *string); 31: #else 32: static void ConvertToLowerCase (/*char *string*/); 33: #endif 34: 1.8 cvs 35: /*---------------------------------------------------------------------- 1.11 cvs 36: ExplodeURL 1.8 cvs 37: ----------------------------------------------------------------------*/ 38: 39: #ifdef __STDC__ 40: void ExplodeURL (char *url, char **proto, char **host, char **dir, char **file) 41: #else 42: void ExplodeURL (url, proto, host, dir, file) 43: char *url; 44: char **proto; 45: char **host; 46: char **dir; 47: char **file; 48: 49: #endif 50: { 1.9 cvs 51: char *curr, *temp; 1.8 cvs 52: 53: if ((url == NULL) || (proto == NULL) || (host == NULL) || 54: (dir == NULL) || (file == NULL)) 55: return; 56: 57: /* initialize every pointer */ 58: *proto = *host = *dir = *file = NULL; 59: 60: /* skip any leading space */ 61: while ((*url == SPACE) || (*url == TAB)) 62: url++; 1.9 cvs 63: curr = url; 64: if (*curr == 0) 1.8 cvs 65: goto finished; 66: 67: /* go to the end of the URL */ 1.9 cvs 68: while ((*curr != 0) && (*curr != SPACE) && (*curr != '\b') && 69: (*curr != '\r') && (*curr != EOL)) 70: curr++; 1.8 cvs 71: 72: /* mark the end of the chain */ 1.9 cvs 73: *curr = EOS; 74: curr--; 75: if (curr <= url) 1.8 cvs 76: goto finished; 77: 78: /* search the next DIR_SEP indicating the beginning of the file name */ 79: do 1.11 cvs 80: curr--; 1.9 cvs 81: while ((curr >= url) && (*curr != DIR_SEP)); 1.11 cvs 82: 1.9 cvs 83: if (curr < url) 1.8 cvs 84: goto finished; 1.9 cvs 85: *file = curr + 1; 1.8 cvs 86: 87: /* mark the end of the dir */ 1.9 cvs 88: *curr = EOS; 89: curr--; 90: if (curr < url) 1.8 cvs 91: goto finished; 92: 93: /* search for the "/" indicating the host name start */ 1.9 cvs 94: while ((curr > url) && ((*curr != DIR_SEP) || (*(curr + 1) != DIR_SEP))) 95: curr--; 1.8 cvs 96: 97: /* if we found it, separate the host name from the directory */ 1.9 cvs 98: if ((*curr == DIR_SEP) && (*(curr + 1) == DIR_SEP)) 1.8 cvs 99: { 1.9 cvs 100: *host = temp = curr + 2; 1.8 cvs 101: while ((*temp != 0) && (*temp != DIR_SEP)) 102: temp++; 103: if (*temp == DIR_SEP) 104: { 105: *temp = EOS; 106: *dir = temp + 1; 107: } 108: } 109: else 1.11 cvs 110: *dir = curr; 111: 1.9 cvs 112: if (curr <= url) 1.8 cvs 113: goto finished; 114: 115: /* mark the end of the proto */ 1.9 cvs 116: *curr = EOS; 117: curr--; 118: if (curr < url) 1.8 cvs 119: goto finished; 120: 1.9 cvs 121: if (*curr == ':') 1.8 cvs 122: { 1.9 cvs 123: *curr = EOS; 124: curr--; 1.8 cvs 125: } 126: else 127: goto finished; 1.11 cvs 128: 1.9 cvs 129: if (curr < url) 1.8 cvs 130: goto finished; 1.9 cvs 131: while ((curr > url) && (isalpha (*curr))) 132: curr--; 133: *proto = curr; 1.8 cvs 134: 135: finished:; 136: 137: #ifdef AMAYA_DEBUG 138: fprintf (stderr, "ExplodeURL(%s)\n\t", url); 139: if (*proto) 140: fprintf (stderr, "proto : %s, ", *proto); 141: if (*host) 142: fprintf (stderr, "host : %s, ", *host); 143: if (*dir) 144: fprintf (stderr, "dir : %s, ", *dir); 145: if (*file) 146: fprintf (stderr, "file : %s ", *file); 147: fprintf (stderr, "\n"); 148: #endif 149: 150: } 1.3 cvs 151: 1.4 cvs 152: /*---------------------------------------------------------------------- 1.9 cvs 153: IsHTMLName 154: returns TRUE if path points to an HTML resource. 1.4 cvs 155: ----------------------------------------------------------------------*/ 1.3 cvs 156: #ifdef __STDC__ 157: boolean IsHTMLName (char *path) 158: #else /* __STDC__ */ 159: boolean IsHTMLName (path) 160: char *path; 161: #endif /* __STDC__ */ 162: { 1.5 cvs 163: char temppath[MAX_LENGTH]; 164: char suffix[MAX_LENGTH]; 165: char nsuffix[MAX_LENGTH]; 166: int i; 167: 168: if (!path) 1.13 cvs 169: return (FALSE); 1.5 cvs 170: 171: strcpy (temppath, path); 172: ExtractSuffix (temppath, suffix); 173: 174: /* Normalize the suffix */ 175: i = 0; 176: while (suffix[i] != EOS) 1.13 cvs 177: { 1.25 cvs 178: nsuffix[i] = tolower (suffix[i]); 1.13 cvs 179: i++; 180: } 1.5 cvs 181: nsuffix[i] = EOS; 182: if ((strcmp (nsuffix, "html")) && 183: (strcmp (nsuffix, "htm")) && 184: (strcmp (nsuffix, "shtml"))) 1.13 cvs 185: return (FALSE); 1.22 cvs 186: else if (!strcmp (nsuffix, "gz")) 1.13 cvs 187: { 188: /* take in account compressed files */ 189: ExtractSuffix (temppath, suffix); 190: /* Normalize the suffix */ 191: i = 0; 192: while (suffix[i] != EOS) 193: { 1.25 cvs 194: nsuffix[i] = tolower (suffix[i]); 1.13 cvs 195: i++; 196: } 197: nsuffix[i] = EOS; 198: if ((strcmp (nsuffix, "html")) && 199: (strcmp (nsuffix, "htm")) && 200: (strcmp (nsuffix, "shtml"))) 201: return (FALSE); 202: else 203: return (TRUE); 204: } 205: else 206: return (TRUE); 1.3 cvs 207: } 208: 1.4 cvs 209: /*---------------------------------------------------------------------- 1.9 cvs 210: IsImageName 211: returns TRUE if path points to an image resource. 1.4 cvs 212: ----------------------------------------------------------------------*/ 1.3 cvs 213: #ifdef __STDC__ 214: boolean IsImageName (char *path) 215: #else /* __STDC__ */ 216: boolean IsImageName (path) 217: char *path; 218: #endif /* __STDC__ */ 219: { 1.5 cvs 220: char temppath[MAX_LENGTH]; 221: char suffix[MAX_LENGTH]; 222: char nsuffix[MAX_LENGTH]; 223: int i; 224: 225: if (!path) 1.13 cvs 226: return (FALSE); 1.5 cvs 227: 228: strcpy (temppath, path); 229: ExtractSuffix (temppath, suffix); 230: 231: /* Normalize the suffix */ 232: i = 0; 233: while (suffix[i] != EOS) 1.13 cvs 234: { 1.25 cvs 235: nsuffix[i] = tolower (suffix[i]); 1.13 cvs 236: i++; 237: } 1.5 cvs 238: nsuffix[i] = EOS; 239: if ((strcmp (nsuffix, "gif")) && (strcmp (nsuffix, "xbm")) && 240: (strcmp (nsuffix, "xpm")) && (strcmp (nsuffix, "jpg")) && 241: (strcmp (nsuffix, "png")) && (strcmp (nsuffix, "au"))) 1.13 cvs 242: return (FALSE); 243: return (TRUE); 1.3 cvs 244: } 245: 1.4 cvs 246: /*---------------------------------------------------------------------- 1.9 cvs 247: IsTextName 1.4 cvs 248: ----------------------------------------------------------------------*/ 1.3 cvs 249: #ifdef __STDC__ 250: boolean IsTextName (char *path) 251: #else /* __STDC__ */ 252: boolean IsTextName (path) 253: char *path; 254: 255: #endif /* __STDC__ */ 256: { 1.5 cvs 257: char temppath[MAX_LENGTH]; 258: char suffix[MAX_LENGTH]; 259: char nsuffix[MAX_LENGTH]; 260: int i; 261: 262: if (!path) 1.13 cvs 263: return (FALSE); 1.5 cvs 264: 265: strcpy (temppath, path); 266: ExtractSuffix (temppath, suffix); 267: 268: /* Normalize the suffix */ 269: i = 0; 270: while (suffix[i] != EOS) 271: { 1.25 cvs 272: nsuffix[i] = tolower (suffix[i]); 1.5 cvs 273: i++; 274: } 275: nsuffix[i] = EOS; 276: 277: if ((strcmp (nsuffix, "gif")) && (strcmp (nsuffix, "xbm")) && 278: (strcmp (nsuffix, "xpm")) && (strcmp (nsuffix, "jpg")) && 279: (strcmp (nsuffix, "pdf")) && (strcmp (nsuffix, "png")) && 1.22 cvs 280: (strcmp (nsuffix, "tgz")) && (strcmp (nsuffix, "tar")) && 281: (strcmp (nsuffix, "xpg")) && (strcmp (nsuffix, "xpd")) && 282: (strcmp (nsuffix, "ps")) && (strcmp (nsuffix, "au"))) 1.13 cvs 283: return (TRUE); 1.22 cvs 284: else if (!strcmp (nsuffix, "gz")) 1.13 cvs 285: { 286: /* take in account compressed files */ 287: ExtractSuffix (temppath, suffix); 288: /* Normalize the suffix */ 289: i = 0; 290: while (suffix[i] != EOS) 291: { 1.25 cvs 292: nsuffix[i] = tolower (suffix[i]); 1.13 cvs 293: i++; 294: } 295: nsuffix[i] = EOS; 296: if ((!strcmp (nsuffix, "html")) || 297: (!strcmp (nsuffix, "htm")) || 298: (!strcmp (nsuffix, "shtml"))) 299: return (TRUE); 300: else 301: return (FALSE); 302: } 303: else 304: return (FALSE); 1.3 cvs 305: } 306: 1.4 cvs 307: /*---------------------------------------------------------------------- 1.9 cvs 308: IsHTTPPath 309: returns TRUE if path is in fact an http URL. 1.4 cvs 310: ----------------------------------------------------------------------*/ 1.3 cvs 311: #ifdef __STDC__ 312: boolean IsHTTPPath (char *path) 313: #else /* __STDC__ */ 314: boolean IsHTTPPath (path) 315: char *path; 316: #endif /* __STDC__ */ 317: { 1.5 cvs 318: if (!path) 319: return FALSE; 1.3 cvs 320: 1.5 cvs 321: if (strncmp (path, "http:", 5) != 0) 322: return FALSE; 323: return TRUE; 1.3 cvs 324: } 325: 1.4 cvs 326: /*---------------------------------------------------------------------- 1.9 cvs 327: IsWithParameters 328: returns TRUE if url has a concatenated query string. 1.4 cvs 329: ----------------------------------------------------------------------*/ 1.3 cvs 330: #ifdef __STDC__ 1.9 cvs 331: boolean IsWithParameters (char *url) 1.3 cvs 332: #else /* __STDC__ */ 1.9 cvs 333: boolean IsWithParameters (url) 334: char *url; 1.3 cvs 335: #endif /* __STDC__ */ 336: { 1.5 cvs 337: int i; 1.3 cvs 338: 1.9 cvs 339: if ((!url) || (url[0] == EOS)) 1.5 cvs 340: return FALSE; 1.3 cvs 341: 1.9 cvs 342: i = strlen (url) - 1; 343: while (i > 0 && url[i--] != '?') 1.5 cvs 344: if (i < 0) 345: return FALSE; 1.3 cvs 346: 1.5 cvs 347: /* There is a parameter */ 348: return TRUE; 1.3 cvs 349: } 350: 1.4 cvs 351: /*---------------------------------------------------------------------- 1.9 cvs 352: IsW3Path 353: returns TRUE if path is in fact a URL. 1.4 cvs 354: ----------------------------------------------------------------------*/ 1.3 cvs 355: #ifdef __STDC__ 356: boolean IsW3Path (char *path) 357: #else /* __STDC__ */ 358: boolean IsW3Path (path) 359: char *path; 360: #endif /* __STDC__ */ 361: { 1.5 cvs 362: if ((strncmp (path, "http:", 5)) && (strncmp (path, "ftp:", 4)) && 363: (strncmp (path, "telnet:", 7)) && (strncmp (path, "wais:", 5)) && 364: (strncmp (path, "news:", 5)) && (strncmp (path, "gopher:", 7)) && 365: (strncmp (path, "mailto:", 7)) && (strncmp (path, "archie:", 7))) 366: return FALSE; 367: return TRUE; 1.3 cvs 368: } 369: 1.4 cvs 370: /*---------------------------------------------------------------------- 1.9 cvs 371: IsValidProtocol 372: returns true if the url protocol is supported by Amaya. 1.4 cvs 373: ----------------------------------------------------------------------*/ 1.3 cvs 374: #ifdef __STDC__ 1.9 cvs 375: boolean IsValidProtocol (char *url) 1.3 cvs 376: #else /* __STDC__ */ 1.9 cvs 377: boolean IsValidProtocol (url) 378: char *url; 1.3 cvs 379: #endif /* __STDC__ */ 380: { 1.26 ! cvs 381: if (!strncmp (url, "http:", 5)) 1.22 cvs 382: /* experimental */ 1.26 ! cvs 383: /*** || !strncmp (url, "ftp:", 4)) ***/ 1.24 cvs 384: /*** || !strncmp (path, "news:", 5)***/ 1.8 cvs 385: return (TRUE); 1.5 cvs 386: else 1.8 cvs 387: return (FALSE); 1.3 cvs 388: } 389: 1.4 cvs 390: /*---------------------------------------------------------------------- 1.9 cvs 391: NormalizeURL 392: normalizes orgName according to a base associated with doc, and 393: following the standard URL format rules. 394: The function returns the new complete and normalized URL 1.12 cvs 395: or file name path (newName) and the name of the document (docName). 1.9 cvs 396: N.B. If the function can't find out what's the docName, it assigns 397: the name "noname.html". 1.4 cvs 398: ----------------------------------------------------------------------*/ 1.3 cvs 399: #ifdef __STDC__ 400: void NormalizeURL (char *orgName, Document doc, char *newName, char *docName) 401: #else /* __STDC__ */ 402: void NormalizeURL (orgName, doc, newName, docName) 403: char *orgName; 404: Document doc; 405: char *newName; 406: char *docName; 407: #endif /* __STDC__ */ 408: { 1.5 cvs 409: char basename[MAX_LENGTH]; 1.18 cvs 410: char tempOrgName[MAX_LENGTH]; 1.5 cvs 411: char *ptr; 412: Element el; 413: ElementType elType; 414: AttributeType attrType; 1.18 cvs 415: Attribute attrHREF = NULL; 1.5 cvs 416: int length; 417: 418: if (!newName || !docName) 419: return; 1.18 cvs 420: 421: /* 422: ** First Step: Clean orgName 423: ** Make sure we have a complete orgName, without any leading or trailing 424: ** white spaces, or trailinbg new lines 425: */ 426: 1.5 cvs 427: ptr = orgName; 1.18 cvs 428: /* skip leading white space and new line characters */ 1.19 cvs 429: while ((*ptr == ' ' || *ptr == EOL) && *ptr++ != EOS); 1.18 cvs 430: strcpy (tempOrgName, ptr); 431: /* clean trailing white space */ 432: ptr = strchr (tempOrgName, ' '); 433: if (ptr) 434: *ptr = EOS; 435: /* clean trailing new lines */ 1.19 cvs 436: ptr = strchr (tempOrgName, EOL); 1.5 cvs 437: if (ptr) 438: *ptr = EOS; 439: 1.18 cvs 440: /* 441: ** Second Step: make orgName a complete URL 442: ** If the URL does not include a protocol, then 443: ** try to calculate one using the doc's base element 444: ** (if it exists), 445: */ 1.21 cvs 446: if (tempOrgName[0] == EOS) 447: { 448: newName[0] = EOS; 449: return; 450: } 451: else if (IsW3Path (tempOrgName)) 452: { 453: /* the name is complete, go to the Sixth Step */ 454: strcpy (newName, tempOrgName); 455: /* verify if the URL has the form "protocol://server:port" */ 1.25 cvs 456: ptr = AmayaParseUrl (newName, "", AMAYA_PARSE_ACCESS | AMAYA_PARSE_HOST | 457: AMAYA_PARSE_PUNCTUATION); 1.21 cvs 458: if (ptr && !strcmp (ptr, newName)) 459: { 460: /* it has this form, we complete it by adding a "/" */ 461: strcat (newName, "/"); 462: } 463: if (ptr) 1.25 cvs 464: TtaFreeMemory (ptr); 1.21 cvs 465: } 466: else if ( doc == 0) 1.19 cvs 467: /* the name is complete, go to the Sixth Step */ 1.18 cvs 468: strcpy (newName, tempOrgName); 1.5 cvs 469: else 470: { 1.18 cvs 471: /* take into account the BASE element. */ 1.22 cvs 472: length = MAX_LENGTH -1; 1.18 cvs 473: /* get the root element */ 474: el = TtaGetMainRoot (doc); 475: 476: /* search the BASE element */ 477: elType.ElSSchema = TtaGetDocumentSSchema (doc); 478: elType.ElTypeNum = HTML_EL_BASE; 479: el = TtaSearchTypedElement (elType, SearchInTree, el); 480: if (el) 1.17 cvs 481: { 1.18 cvs 482: /* 483: ** The document has a BASE element 484: ** Get the HREF attribute of the BASE Element 485: */ 486: attrType.AttrSSchema = elType.ElSSchema; 487: attrType.AttrTypeNum = HTML_ATTR_HREF_; 488: attrHREF = TtaGetAttribute (el, attrType); 489: if (attrHREF) 1.14 cvs 490: { 1.18 cvs 491: /* Use the base path of the document */ 492: TtaGiveTextAttributeValue (attrHREF, basename, &length); 493: /* base and orgName have to be separated by a DIR_SEP */ 1.20 cvs 494: length--; 1.18 cvs 495: if (basename[0] != EOS && basename[length] != '/') 496: /* verify if the base has the form "protocol://server:port" */ 1.14 cvs 497: { 1.25 cvs 498: ptr = AmayaParseUrl (basename, "", AMAYA_PARSE_ACCESS | AMAYA_PARSE_HOST | 499: AMAYA_PARSE_PUNCTUATION); 1.18 cvs 500: if (ptr && !strcmp (ptr, basename)) 1.14 cvs 501: { 1.18 cvs 502: /* it has this form, we complete it by adding a "/" */ 503: strcat (basename, "/"); 504: length++; 1.14 cvs 505: } 1.18 cvs 506: if (ptr) 1.25 cvs 507: TtaFreeMemory (ptr); 1.14 cvs 508: } 1.19 cvs 509: /* Third Step: prepare the base 510: ** Removing anything after the 511: ** last DIR_SEP char. If no such char is found, then search for 512: ** the first ":" char, hoping that what's before that is a 513: ** protocol. If found, end the string there. If neither 514: ** char is found, then discard the whole base element. 515: */ 516: 517: /* search for the last DIR_SEP char */ 1.18 cvs 518: while (length >= 0 && basename[length] != DIR_SEP) 1.19 cvs 519: length--; 520: if (length >= 0) 521: /* found the last DIR_SEP char, end the string there */ 522: basename[length + 1] = EOS; 523: else 524: /* search for the first ":" char */ 525: { 526: for (length = 0; basename[length] != ':' && 1.20 cvs 527: basename[length] != EOS; length++); 1.19 cvs 528: if (basename[length] == ':') 529: /* found, so end the string there */ 530: basename[length + 1] = EOS; 531: else 532: /* not found, discard the base */ 533: basename[0] = EOS; 534: } 1.14 cvs 535: } 536: else 537: basename[0] = EOS; 1.18 cvs 538: } 1.22 cvs 539: 1.18 cvs 540: /* 1.19 cvs 541: ** Fourth Step: 1.18 cvs 542: ** If there's no base element, and if we're following 1.19 cvs 543: ** a link, use the URL of the current document as a base. 1.18 cvs 544: */ 545: 546: if (!attrHREF) 547: { 548: if (DocumentURLs[(int) doc]) 1.14 cvs 549: { 1.18 cvs 550: strcpy (basename, DocumentURLs[(int) doc]); 551: /* base and orgName have to be separated by a DIR_SEP */ 552: length = strlen (basename) - 1; 1.19 cvs 553: /* search for the last DIR_SEP char */ 1.18 cvs 554: while (length >= 0 && basename[length] != DIR_SEP) 1.19 cvs 555: length--; 556: if (length >= 0) 557: /* found the last DIR_SEP char, end the string there */ 558: basename[length + 1] = EOS; 559: else 560: /* search for the first ":" char */ 561: { 562: for (length = 0; basename[length] != ':' && 563: basename[length] != EOS; length ++); 564: if (basename[length] == ':') 565: /* found, so end the string there */ 566: basename[length + 1] = EOS; 567: else 568: /* not found, discard the base */ 569: basename[0] = EOS; 570: } 1.14 cvs 571: } 572: else 1.19 cvs 573: basename[0] = EOS; 1.14 cvs 574: } 1.22 cvs 575: 1.18 cvs 576: /* 1.19 cvs 577: ** Fifth Step, calculate the absolute URL, using the base 1.18 cvs 578: */ 579: 1.25 cvs 580: ptr = AmayaParseUrl (tempOrgName, basename, AMAYA_PARSE_ALL); 1.16 cvs 581: 1.14 cvs 582: if (ptr) 583: { 1.25 cvs 584: ptr = AmayaSimplifyUrl (&ptr); 1.14 cvs 585: strcpy (newName, ptr); 1.25 cvs 586: TtaFreeMemory (ptr); 1.14 cvs 587: } 588: else 1.18 cvs 589: newName[0] = EOS; 1.5 cvs 590: } 591: 1.18 cvs 592: /* 1.19 cvs 593: ** Sixth and last Step: 1.18 cvs 594: ** Prepare the docname that will refer to this ressource in the 1.19 cvs 595: ** .amaya directory. If the new URL finishes on DIR_SEP, then use 1.18 cvs 596: ** noname.html as a default ressource name 597: */ 1.19 cvs 598: 599: if (newName[0] != EOS) 1.5 cvs 600: { 1.19 cvs 601: length = strlen (newName) - 1; 1.18 cvs 602: if (newName[length] == DIR_SEP) 603: { 604: /* docname was not comprised inside the URL, so let's */ 605: /* assign the default ressource name */ 606: strcpy (docName, "noname.html"); 607: /* remove DIR_SEP at the end of complete path */ 1.23 cvs 608: /* newName[length] = EOS; */ 1.18 cvs 609: } 1.14 cvs 610: else 1.18 cvs 611: { 612: /* docname is comprised inside the URL */ 613: while (length >= 0 && newName[length] != DIR_SEP) 614: length--; 615: if (length < 0) 616: strcpy (docName, newName); 617: else 618: strcpy (docName, &newName[length+1]); 619: } 1.19 cvs 620: 1.5 cvs 621: } 1.18 cvs 622: else 623: docName[0] = EOS; 624: } 1.3 cvs 625: 1.4 cvs 626: /*---------------------------------------------------------------------- 1.9 cvs 627: IsSameHost 1.4 cvs 628: ----------------------------------------------------------------------*/ 1.3 cvs 629: #ifdef __STDC__ 630: boolean IsSameHost (char *url1, char *url2) 631: #else /* __STDC__ */ 632: boolean IsSameHost (url1, url2) 633: char *path; 634: #endif /* __STDC__ */ 635: { 1.5 cvs 636: char *basename_ptr1, *basename_ptr2; 637: boolean result; 1.3 cvs 638: 1.25 cvs 639: basename_ptr1 = AmayaParseUrl (url1, "", AMAYA_PARSE_ACCESS | AMAYA_PARSE_HOST | AMAYA_PARSE_PUNCTUATION); 640: basename_ptr2 = AmayaParseUrl (url2, "", AMAYA_PARSE_ACCESS | AMAYA_PARSE_HOST | AMAYA_PARSE_PUNCTUATION); 1.3 cvs 641: 1.5 cvs 642: if (strcmp (basename_ptr1, basename_ptr2)) 1.8 cvs 643: result = FALSE; 1.5 cvs 644: else 1.8 cvs 645: result = TRUE; 1.3 cvs 646: 1.25 cvs 647: TtaFreeMemory (basename_ptr1); 648: TtaFreeMemory (basename_ptr2); 1.3 cvs 649: 1.5 cvs 650: return (result); 1.3 cvs 651: } 652: 653: 1.4 cvs 654: /*---------------------------------------------------------------------- 1.9 cvs 655: AHTMakeRelativeURL 656: converts url into a relative url to base_url. 657: If succesful, returns the new URL, otherwise, it returns NULL. 658: The caller has to free the new URL. 1.4 cvs 659: ----------------------------------------------------------------------*/ 1.3 cvs 660: #ifdef __STDC__ 1.5 cvs 661: char *AHTMakeRelativeName (char *url, char *base_url) 1.3 cvs 662: #else /* __STDC__ */ 1.5 cvs 663: char *AHTMakeRelativeName (url, base_url) 664: char url; 665: char base_url; 666: 1.3 cvs 667: #endif /* __STDC__ */ 668: { 1.5 cvs 669: char *base_ptr, *url_ptr; 670: char *result; 671: 672: /* verify if we are in the same host */ 1.3 cvs 673: 1.25 cvs 674: base_ptr = AmayaParseUrl (base_url, "", AMAYA_PARSE_ACCESS | AMAYA_PARSE_HOST | AMAYA_PARSE_PUNCTUATION); 675: url_ptr = AmayaParseUrl (url, "", AMAYA_PARSE_ACCESS | AMAYA_PARSE_HOST | AMAYA_PARSE_PUNCTUATION); 1.3 cvs 676: 1.5 cvs 677: if (!strcmp (base_ptr, url_ptr)) 678: { 1.25 cvs 679: TtaFreeMemory (base_ptr); 680: TtaFreeMemory (url_ptr); 1.3 cvs 681: 1.5 cvs 682: /* Normalize the URLs */ 1.3 cvs 683: 1.25 cvs 684: base_ptr = AmayaParseUrl (base_url, "", AMAYA_PARSE_ALL); 685: url_ptr = AmayaParseUrl (url, "", AMAYA_PARSE_ALL); 1.3 cvs 686: 1.5 cvs 687: /* Use libwww to make relative name */ 1.3 cvs 688: 1.25 cvs 689: result = AmayaRelativeUrl (url_ptr, base_ptr); 690: TtaFreeMemory (base_ptr); 691: TtaFreeMemory (url_ptr); 1.5 cvs 692: } 693: else 694: result = (char *) NULL; 1.3 cvs 695: 1.5 cvs 696: return (result); 1.3 cvs 697: } 1.22 cvs 698: /*---------------------------------------------------------------------- 699: HasKnownFileSuffix 700: returns TRUE if path points to a file ending with a suffix. 701: ----------------------------------------------------------------------*/ 702: #ifdef __STDC__ 703: boolean HasKnownFileSuffix (char *path) 704: #else /* __STDC__ */ 705: boolean HasKnownFileSuffix (path) 706: char *path; 707: #endif /* __STDC__ */ 708: { 709: char *root; 710: char temppath[MAX_LENGTH]; 711: char suffix[MAX_LENGTH]; 712: 1.24 cvs 713: if (!path || path[0] == EOS || path[strlen(path)] == DIR_SEP) 1.22 cvs 714: return (FALSE); 715: 1.25 cvs 716: root = AmayaParseUrl(path, (char *) NULL, AMAYA_PARSE_PATH | AMAYA_PARSE_PUNCTUATION); 1.22 cvs 717: 718: if (root) 719: { 720: strcpy (temppath, root); 1.25 cvs 721: TtaFreeMemory (root); 1.22 cvs 722: /* Get the suffix */ 723: ExtractSuffix (temppath, suffix); 724: 725: if( suffix[0] == EOS) 726: /* no suffix */ 727: return (FALSE); 728: 729: /* Normalize the suffix */ 730: ConvertToLowerCase (suffix); 731: 1.23 cvs 732: if (!strcmp (suffix, "gz")) 1.22 cvs 733: /* skip the compressed suffix */ 734: { 735: ExtractSuffix (temppath, suffix); 736: if(suffix[0] == EOS) 737: /* no suffix */ 738: return (FALSE); 739: /* Normalize the suffix */ 740: ConvertToLowerCase (suffix); 741: } 742: 743: if ((strcmp (suffix, "gif")) && (strcmp (suffix, "xbm")) && 744: (strcmp (suffix, "xpm")) && (strcmp (suffix, "jpg")) && 745: (strcmp (suffix, "pdf")) && (strcmp (suffix, "png")) && 746: (strcmp (suffix, "tgz")) && (strcmp (suffix, "xpg")) && 747: (strcmp (suffix, "xpd")) && (strcmp (suffix, "ps")) && 748: (strcmp (suffix, "au")) && (strcmp (suffix, "html")) && 749: (strcmp (suffix, "htm")) && (strcmp (suffix, "shtml")) && 750: (strcmp (suffix, "txt")) && (strcmp (suffix, "css")) && 751: (strcmp (suffix, "eps"))) 752: return (FALSE); 753: else 754: return (TRUE); 755: } 756: else 757: return (FALSE); 758: } 759: 760: 761: /*---------------------------------------------------------------------- 762: ConvertToLowerCase 763: Converts a string to lowercase. 764: ----------------------------------------------------------------------*/ 765: #ifdef __STDC__ 766: static void ConvertToLowerCase (char *string) 767: #else /* __STDC__ */ 768: static void ConvertToLowerCase (string) 769: char *string; 770: 771: #endif /* __STDC__ */ 772: { 773: int i; 774: 775: if (!string) 776: return; 777: 778: for (i = 0; string[i] != EOS; i++) 1.25 cvs 779: string[i] = tolower (string[i]); 1.22 cvs 780: } 781: 782: 1.24 cvs 783: /*---------------------------------------------------------------------- 784: ChopURL 785: Gives back a URL no longer than MAX_PRINT_URL_LENGTH chars (outputURL). 786: If inputURL is bigger than that size, outputURL receives 787: MAX_PRINT_URL_LENGTH / 2 chars from the beginning of inputURL, "...", 788: and MAX_PRINT_URL_LENGTH / 2 chars from the end of inputURL. 789: If inputURL is not longer than MAX_PRINT_URL_LENGTH chars, it gets 790: copied into outputURL. 791: N.B.: outputURL must point to a memory block of MAX_PRINT_URL_LENGTH 792: chars. 793: ----------------------------------------------------------------------*/ 794: #ifdef __STDC__ 795: void ChopURL (char *outputURL, char *inputURL) 796: #else 797: void ChopURL (outputURL, inputURL) 798: char *outputURL; 799: char *inputURL; 800: #endif 1.22 cvs 801: 1.24 cvs 802: { 803: int len; 1.9 cvs 804: 1.24 cvs 805: len = strlen (inputURL); 806: if (len <= MAX_PRINT_URL_LENGTH) 807: { 808: strcpy (outputURL, inputURL); 809: } 810: else 811: /* make a truncated urlName on the status window */ 812: { 813: strncpy (outputURL, inputURL, MAX_PRINT_URL_LENGTH / 2); 814: outputURL [MAX_PRINT_URL_LENGTH / 2] = EOS; 815: strcat (outputURL, "..."); 816: strcat (outputURL, &(inputURL[len - MAX_PRINT_URL_LENGTH / 2 ])); 817: } 1.25 cvs 818: } 819: 820: /************************************************************************ 821: * * 822: * Local Adaptation of the libWWW Library/src/AmayaParseUrl.c code. * 823: * * 824: ************************************************************************/ 825: 826: #define StringAllocCopy(dest,src) { \ 827: if (src == NULL) dest = NULL; \ 828: else { \ 829: if ((dest = (char *) TtaGetMemory(strlen(src) + 1)) == NULL) \ 830: exit(1); \ 831: else strcpy(dest, src); }} \ 832: 833: typedef struct _HTURI { 834: char * access; /* Now known as "scheme" */ 835: char * host; 836: char * absolute; 837: char * relative; 838: char * fragment; 839: } HTURI; 840: 841: /*---------------------------------------------------------------------- 842: scan 843: Scan a filename for its consituents 844: ----------------------------------- 845: 846: On entry, 847: name points to a document name which may be incomplete. 848: On exit, 849: absolute or relative may be nonzero (but not both). 850: host, fragment and access may be nonzero if they were specified. 851: Any which are nonzero point to zero terminated strings. 852: ----------------------------------------------------------------------*/ 853: #ifdef __STDC__ 854: static void scan (char * name, HTURI * parts) 855: #else /* __STDC__ */ 856: static void scan (name, parts) 857: char *name; 858: HTURI *parts; 859: 860: #endif /* __STDC__ */ 861: { 862: char * p; 863: char * after_access = name; 864: memset(parts, '\0', sizeof(HTURI)); 865: 866: /* Look for fragment identifier */ 867: if ((p = strrchr(name, '#')) != NULL) { 868: *p++ = '\0'; 869: parts->fragment = p; 870: } 871: 872: for(p=name; *p; p++) { 873: if (*p=='/' || *p=='#' || *p=='?') 874: break; 875: if (*p==':') { 876: *p = 0; 877: parts->access = after_access; /* Scheme has been specified */ 878: 879: /* The combination of gcc, the "-O" flag and the HP platform is 880: unhealthy. The following three lines is a quick & dirty fix, but is 881: not recommended. Rather, turn off "-O". */ 882: 883: /* after_access = p;*/ 884: /* while (*after_access == 0)*/ 885: /* after_access++;*/ 886: 887: after_access = p+1; 888: 889: if (0==strcasecmp("URL", parts->access)) { 890: parts->access = NULL; /* Ignore IETF's URL: pre-prefix */ 891: } else break; 892: } 893: } 894: 895: p = after_access; 896: if (*p=='/'){ 897: if (p[1]=='/') { 898: parts->host = p+2; /* host has been specified */ 899: *p=0; /* Terminate access */ 900: p=strchr(parts->host,'/'); /* look for end of host name if any */ 901: if(p) { 902: *p=0; /* Terminate host */ 903: parts->absolute = p+1; /* Root has been found */ 904: } 905: } else { 906: parts->absolute = p+1; /* Root found but no host */ 907: } 908: } else { 909: parts->relative = (*after_access) ? after_access : 0; /* zero for "" */ 910: } 911: } 912: 913: 914: /*---------------------------------------------------------------------- 915: AmayaParseUrl 916: Parse a Name relative to another name 917: ------------------------------------- 918: 919: This returns those parts of a name which are given (and requested) 920: substituting bits from the related name where necessary. 921: 922: On entry, 923: aName A filename given 924: relatedName A name relative to which aName is to be parsed. Give 925: it an empty string if aName is absolute. 926: wanted A mask for the bits which are wanted. 927: 928: On exit, 929: returns A pointer to a malloc'd string which MUST BE FREED 930: 931: ----------------------------------------------------------------------*/ 932: #ifdef __STDC__ 933: char * AmayaParseUrl (const char *aName, const char *relatedName, int wanted) 934: #else /* __STDC__ */ 935: char * AmayaParseUrl (aName, relatedName, wanted) 936: const char *aName; 937: const char *relatedName; 938: int wanted; 939: 940: #endif /* __STDC__ */ 941: { 942: char * result = 0; 943: char * return_value = 0; 944: int len; 945: char * name = 0; 946: char * rel = 0; 947: char * p; 948: char * access; 949: HTURI given, related; 950: 951: if (!relatedName) /* HWL 23/8/94: dont dump due to NULL */ 952: relatedName = ""; 953: 954: /* Make working copies of input strings to cut up: */ 955: len = strlen(aName)+strlen(relatedName)+10; 956: if ((result=(char *) TtaGetMemory(len)) == NULL) /* Lots of space: more than enough */ 957: exit(1); 958: StringAllocCopy(name, aName); 959: StringAllocCopy(rel, relatedName); 960: 961: scan(name, &given); 962: scan(rel, &related); 963: result[0]=0; /* Clear string */ 964: access = given.access ? given.access : related.access; 965: if (wanted & AMAYA_PARSE_ACCESS) 966: if (access) { 967: strcat(result, access); 968: if(wanted & AMAYA_PARSE_PUNCTUATION) strcat(result, ":"); 969: } 970: 971: if (given.access && related.access) /* If different, inherit nothing. */ 972: if (strcmp(given.access, related.access)!=0) { 973: related.host=0; 974: related.absolute=0; 975: related.relative=0; 976: related.fragment=0; 977: } 978: 979: if (wanted & AMAYA_PARSE_HOST) 980: if(given.host || related.host) { 981: if(wanted & AMAYA_PARSE_PUNCTUATION) strcat(result, "//"); 982: strcat(result, given.host ? given.host : related.host); 983: } 984: 985: if (given.host && related.host) /* If different hosts, inherit no path. */ 986: if (strcmp(given.host, related.host)!=0) { 987: related.absolute=0; 988: related.relative=0; 989: related.fragment=0; 990: } 991: 992: if (wanted & AMAYA_PARSE_PATH) { 993: if(given.absolute) { /* All is given */ 994: if(wanted & AMAYA_PARSE_PUNCTUATION) strcat(result, "/"); 995: strcat(result, given.absolute); 996: } else if(related.absolute) { /* Adopt path not name */ 997: strcat(result, "/"); 998: strcat(result, related.absolute); 999: if (given.relative) { 1000: p = strchr(result, '?'); /* Search part? */ 1001: if (!p) p=result+strlen(result)-1; 1002: for (; *p!='/'; p--); /* last / */ 1003: p[1]=0; /* Remove filename */ 1004: strcat(result, given.relative); /* Add given one */ 1005: #if 0 1006: result = AmayaSimplifyUrl (&result); 1007: #endif 1008: } 1009: } else if(given.relative) { 1010: strcat(result, given.relative); /* what we've got */ 1011: } else if(related.relative) { 1012: strcat(result, related.relative); 1013: } else { /* No inheritance */ 1014: strcat(result, "/"); 1015: } 1016: } 1017: 1018: if (wanted & AMAYA_PARSE_ANCHOR) 1019: if(given.fragment || related.fragment) { 1020: if(given.absolute && given.fragment) { /*Fixes for relURLs...*/ 1021: if(wanted & AMAYA_PARSE_PUNCTUATION) strcat(result, "#"); 1022: strcat(result, given.fragment); 1023: } else if (!(given.absolute) && !(given.fragment)) { 1024: strcat(result, ""); 1025: } else { 1026: if(wanted & AMAYA_PARSE_PUNCTUATION) strcat(result, "#"); 1027: strcat(result, given.fragment ? given.fragment : related.fragment); 1028: } 1029: } 1030: TtaFreeMemory(rel); 1031: TtaFreeMemory(name); 1032: 1033: StringAllocCopy(return_value, result); 1034: TtaFreeMemory(result); 1035: return return_value; /* exactly the right length */ 1036: } 1037: 1038: /*---------------------------------------------------------------------- 1039: HTCanon 1040: Canonicalizes the URL in the following manner starting from the host 1041: pointer: 1042: 1043: 1) The host name is converted to lowercase 1044: 2) Chop off port if `:80' (http), `:70' (gopher), or `:21' (ftp) 1045: 1046: Return: OK The position of the current path part of the URL 1047: which might be the old one or a new one. 1048: 1049: ----------------------------------------------------------------------*/ 1050: #ifdef __STDC__ 1051: static char * HTCanon (char ** filename, char * host) 1052: #else /* __STDC__ */ 1053: static char * HTCanon (filename,host) 1054: char **filename; 1055: char *host; 1056: #endif /* __STDC__ */ 1057: { 1058: char *newname = NULL; 1059: char *port; 1060: char *strptr; 1061: char *path; 1062: char *access = host-3; 1063: 1064: while (access>*filename && *(access-1)!='/') /* Find access method */ 1065: access--; 1066: if ((path = strchr(host, '/')) == NULL) /* Find path */ 1067: path = host + strlen(host); 1068: if ((strptr = strchr(host, '@')) != NULL && strptr<path) /* UserId */ 1069: host = strptr; 1070: if ((port = strchr(host, ':')) != NULL && port>path) /* Port number */ 1071: port = NULL; 1072: 1073: strptr = host; /* Convert to lower-case */ 1074: while (strptr<path) { 1075: *strptr = tolower(*strptr); 1076: strptr++; 1077: } 1078: 1079: /* Does the URL contain a full domain name? This also works for a 1080: numerical host name. The domain name is already made lower-case 1081: and without a trailing dot. */ 1082: { 1083: char *dot = port ? port : path; 1084: if (dot > *filename && *--dot=='.') { 1085: char *orig=dot, *dest=dot+1; 1086: while((*orig++ = *dest++)); 1087: if (port) port--; 1088: path--; 1089: } 1090: } 1091: /* Chop off port if `:', `:80' (http), `:70' (gopher), or `:21' (ftp) */ 1092: if (port) { 1093: if (!*(port+1) || *(port+1)=='/') { 1094: if (!newname) { 1095: char *orig=port, *dest=port+1; 1096: while((*orig++ = *dest++)); 1097: } 1098: } else if ((!strncmp(access, "http", 4) && 1099: (*(port+1)=='8'&&*(port+2)=='0'&&(*(port+3)=='/'||!*(port+3)))) || 1100: (!strncmp(access, "gopher", 6) && 1101: (*(port+1)=='7'&&*(port+2)=='0'&&(*(port+3)=='/'||!*(port+3)))) || 1102: (!strncmp(access, "ftp", 3) && 1103: (*(port+1)=='2'&&*(port+2)=='1'&&(*(port+3)=='/'||!*(port+3))))) { 1104: if (!newname) { 1105: char *orig=port, *dest=port+3; 1106: while((*orig++ = *dest++)); 1107: path -= 3; /* Update path position, Henry Minsky */ 1108: } 1109: } else if (newname) 1110: strncat(newname, port, (int) (path-port)); 1111: } 1112: 1113: if (newname) { 1114: char *newpath = newname+strlen(newname); 1115: strcat(newname, path); 1116: path = newpath; 1117: TtaFreeMemory(*filename); /* Free old copy */ 1118: *filename = newname; 1119: } 1120: return path; 1121: } 1122: 1123: 1124: /*---------------------------------------------------------------------- 1125: AmayaSimplifyUrl 1126: Simplify a URI 1127: -------------- 1128: A URI is allowed to contain the seqeunce xxx/../ which may be 1129: replaced by "" , and the seqeunce "/./" which may be replaced by "/". 1130: Simplification helps us recognize duplicate URIs. 1131: 1132: Thus, /etc/junk/../fred becomes /etc/fred 1133: /etc/junk/./fred becomes /etc/junk/fred 1134: 1135: but we should NOT change 1136: http://fred.xxx.edu/../.. 1137: 1138: or ../../albert.html 1139: 1140: In order to avoid empty URLs the following URLs become: 1141: 1142: /fred/.. becomes /fred/.. 1143: /fred/././.. becomes /fred/.. 1144: /fred/.././junk/.././ becomes /fred/.. 1145: 1146: If more than one set of `://' is found (several proxies in cascade) then 1147: only the part after the last `://' is simplified. 1148: 1149: Returns: A string which might be the old one or a new one. 1150: 1151: ----------------------------------------------------------------------*/ 1152: #ifdef __STDC__ 1153: char *AmayaSimplifyUrl (char ** url) 1154: #else /* __STDC__ */ 1155: char *AmayaSimplifyUrl (url) 1156: char **url; 1157: #endif /* __STDC__ */ 1158: { 1159: char *path; 1160: char *p; 1161: if (!url || !*url) { 1162: return *url; 1163: } 1164: 1165: /* Find any scheme name */ 1166: if ((path = strstr(*url, "://")) != NULL) { /* Find host name */ 1167: char *newptr; 1168: char *access = *url; 1169: while (access<path && (*access=tolower(*access))) access++; 1170: path += 3; 1171: while ((newptr = strstr(path, "://")) != NULL) /* For proxies */ 1172: path = newptr+3; 1173: path = HTCanon(url, path); /* We have a host name */ 1174: } else if ((path = strstr(*url, ":/")) != NULL) { 1175: path += 2; 1176: } else 1177: path = *url; 1178: if (*path == '/' && *(path+1)=='/') { /* Some URLs start //<foo> */ 1179: path += 1; 1180: } else if (!strncmp(path, "news:", 5)) { 1181: char *ptr = strchr(path+5, '@'); 1182: if (!ptr) ptr = path+5; 1183: while (*ptr) { /* Make group or host lower case */ 1184: *ptr = tolower(*ptr); 1185: ptr++; 1186: } 1187: return *url; /* Doesn't need to do any more */ 1188: } 1189: if ((p = path)) { 1190: char *end; 1191: if (!((end = strchr(path, ';')) || (end = strchr(path, '?')) || 1192: (end = strchr(path, '#')))) 1193: end = path+strlen(path); 1194: 1195: /* Parse string second time to simplify */ 1196: p = path; 1197: while(p<end) { 1198: if (*p=='/') { 1199: if (p>*url && *(p+1)=='.' && (*(p+2)=='/' || !*(p+2))) { 1200: char *orig = p+1; 1201: char *dest = (*(p+2)!='/') ? p+2 : p+3; 1202: while ((*orig++ = *dest++)); /* Remove a slash and a dot */ 1203: end = orig-1; 1204: } else if (*(p+1)=='.' && *(p+2)=='.' && (*(p+3)=='/' || !*(p+3))) { 1205: char *q = p; 1206: while (q>path && *--q!='/'); /* prev slash */ 1207: if (strncmp(q, "/../", 4)) { 1208: char *orig = q+1; 1209: char *dest = (*(p+3)!='/') ? p+3 : p+4; 1210: while ((*orig++ = *dest++)); /* Remove /xxx/.. */ 1211: end = orig-1; 1212: p = q; /* Start again with prev slash */ 1213: } else 1214: p++; 1215: } else if (*(p+1)=='/') { 1216: while (*(p+1)=='/') { 1217: char *orig=p, *dest=p+1; 1218: while ((*orig++ = *dest++)); /* Remove multiple /'s */ 1219: end = orig-1; 1220: } 1221: } else 1222: p++; 1223: } else 1224: p++; 1225: } 1226: } 1227: return *url; 1228: } 1229: 1230: /*---------------------------------------------------------------------- 1231: AmayaRelativeUrl 1232: Make Relative Name 1233: ------------------ 1234: 1235: This function creates and returns a string which gives an expression of 1236: one address as related to another. Where there is no relation, an absolute 1237: address is retured. 1238: 1239: On entry, 1240: Both names must be absolute, fully qualified names of nodes 1241: (no fragment bits) 1242: 1243: On exit, 1244: The return result points to a newly allocated name which, if 1245: parsed by AmayaParseUrl relative to relatedName, will yield aName. 1246: The caller is responsible for freeing the resulting name later. 1247: 1248: ----------------------------------------------------------------------*/ 1249: #ifdef __STDC__ 1250: char * AmayaRelativeUrl (const char * aName, const char * relatedName) 1251: #else /* __STDC__ */ 1252: char * AmayaRelativeUrl (const char * aName, const char * relatedName) 1253: const char *aName; 1254: #endif /* __STDC__ */ 1255: { 1256: char * result = 0; 1257: const char *p = aName; 1258: const char *q = relatedName; 1259: const char * after_access = 0; 1260: const char * path = 0; 1261: const char * last_slash = 0; 1262: int slashes = 0; 1263: 1264: for(;*p; p++, q++) { /* Find extent of match */ 1265: if (*p!=*q) break; 1266: if (*p==':') after_access = p+1; 1267: if (*p=='/') { 1268: last_slash = p; 1269: slashes++; 1270: if (slashes==3) path=p; 1271: } 1272: } 1273: 1274: /* q, p point to the first non-matching character or zero */ 1275: 1276: if (!after_access) { /* Different access */ 1277: StringAllocCopy(result, aName); 1278: } else if (slashes<3){ /* Different nodes */ 1279: StringAllocCopy(result, after_access); 1280: } else { /* Some path in common */ 1281: int levels= 0; 1282: for(; *q && (*q!='#'); q++) if (*q=='/') levels++; 1283: if ((result = (char *) TtaGetMemory(3*levels + strlen(last_slash) + 1)) == NULL) 1284: exit(1); 1285: result[0]=0; 1286: for(;levels; levels--)strcat(result, "../"); 1287: strcat(result, last_slash+1); 1288: } 1289: return result; 1.24 cvs 1290: } 1.9 cvs 1291: 1292: 1.24 cvs 1293: /* 1294: end of Module AHTURLTools.c 1295: */