Merge branch 'master' of https://git.maemo.org/projects/erwise
[erwise] / WWWLibrary / HTParse.c
1 /*              Parse HyperText Document Address                HTParse.c
2 **              ================================
3 */
4
5 #include "HTUtils.h"
6 #include "HTParse.h"
7 #include "tcp.h"
8
9 struct struct_parts {
10         char * access;
11         char * host;
12         char * absolute;
13         char * relative;
14 /*      char * search;          no - treated as part of path */
15         char * anchor;
16 };
17
18
19 /*      Strip white space off a string
20 **      ------------------------------
21 **
22 ** On exit,
23 **      Return value points to first non-white character, or to 0 if none.
24 **      All trailing white space is OVERWRITTEN with zero.
25 */
26
27 #ifdef __STDC__
28 char * HTStrip(char * s)
29 #else
30 char * HTStrip(s)
31         char *s;
32 #endif
33 {
34 #define SPACE(c) ((c==' ')||(c=='\t')||(c=='\n')) 
35     char * p=s;
36     for(p=s;*p;p++);                    /* Find end of string */
37     for(p--;p>=s;p--) {
38         if(SPACE(*p)) *p=0;     /* Zap trailing blanks */
39         else break;
40     }
41     while(SPACE(*s))s++;        /* Strip leading blanks */
42     return s;
43 }
44
45
46 /*      Scan a filename for its consituents
47 **      -----------------------------------
48 **
49 ** On entry,
50 **      name    points to a document name which may be incomplete.
51 ** On exit,
52 **      absolute or relative may be nonzero (but not both).
53 **      host, anchor and access may be nonzero if they were specified.
54 **      Any which are nonzero point to zero terminated strings.
55 */
56 #ifdef __STDC__
57 PRIVATE void scan(char * name, struct struct_parts *parts)
58 #else
59 PRIVATE void scan(name, parts)
60     char * name;
61     struct struct_parts *parts;
62 #endif
63 {
64     char * after_access;
65     char * p;
66     int length = strlen(name);
67     
68     parts->access = 0;
69     parts->host = 0;
70     parts->absolute = 0;
71     parts->relative = 0;
72     parts->anchor = 0;
73     
74     after_access = name;
75     for(p=name; *p; p++) {
76         if (*p==':') {
77                 *p = 0;
78                 parts->access = name;   /* Access name has been specified */
79                 after_access = p+1;
80         }
81         if (*p=='/') break;
82         if (*p=='#') break;
83     }
84     
85     for(p=name+length-1; p>=name; p--) {
86         if (*p =='#') {
87             parts->anchor=p+1;
88             *p=0;                               /* terminate the rest */
89         }
90     }
91     p = after_access;
92     if (*p=='/'){
93         if (p[1]=='/') {
94             parts->host = p+2;          /* host has been specified      */
95             *p=0;                       /* Terminate access             */
96             p=strchr(parts->host,'/');  /* look for end of host name if any */
97             if(p) {
98                 *p=0;                   /* Terminate host */
99                 parts->absolute = p+1;          /* Root has been found */
100             }
101         } else {
102             parts->absolute = p+1;              /* Root found but no host */
103         }           
104     } else {
105         parts->relative = (*after_access) ? after_access : 0;   /* zero for "" */
106     }
107 #ifdef NOT_DEFINED      /* search is just treated as part of path */
108     {
109         char *p = relative ? relative : absolute;
110         if (p) {
111             char * q = strchr(p, '?');  /* Any search string? */
112             if (q) {
113                 *q = 0;                 /* If so, chop that off. */
114                 parts->search = q+1;
115             }
116         }
117     }
118 #endif
119 } /*scan */    
120
121
122 /*      Parse a Name relative to another name
123 **      -------------------------------------
124 **
125 **      This returns those parts of a name which are given (and requested)
126 **      substituting bits from the related name where necessary.
127 **
128 ** On entry,
129 **      aName           A filename given
130 **      relatedName     A name relative to which aName is to be parsed
131 **      wanted          A mask for the bits which are wanted.
132 **
133 ** On exit,
134 **      returns         A pointer to a malloc'd string which MUST BE FREED
135 */
136 #ifdef __STDC__
137 char * HTParse(const char * aName, const char * relatedName, int wanted)
138 #else
139 char * HTParse(aName, relatedName, wanted)
140     char * aName;
141     char * relatedName;
142     int wanted;
143 #endif
144
145 {
146     char * result = 0;
147     char * return_value = 0;
148     int len;
149     char * name = 0;
150     char * rel = 0;
151     char * p;
152     struct struct_parts given, related;
153     
154     /* Make working copies of input strings to cut up:
155     */
156     len = strlen(aName)+strlen(relatedName)+10;
157     result=(char *)malloc(len);         /* Lots of space: more than enough */
158     if (result == NULL) outofmem(__FILE__, "HTParse");
159     
160     StrAllocCopy(name, aName);
161     StrAllocCopy(rel, relatedName);
162     
163     scan(name, &given);
164     scan(rel,  &related); 
165     result[0]=0;                /* Clear string  */
166     if (wanted & PARSE_ACCESS)
167         if (given.access|| related.access) {
168             strcat(result, given.access ? given.access : related.access);
169             if(wanted & PARSE_PUNCTUATION) strcat(result, ":");
170         }
171         
172     if (given.access && related.access) /* If different, inherit nothing. */
173         if (strcmp(given.access, related.access)!=0) {
174             related.host=0;
175             related.absolute=0;
176             related.relative=0;
177             related.anchor=0;
178         }
179         
180     if (wanted & PARSE_HOST)
181         if(given.host || related.host) {
182             if(wanted & PARSE_PUNCTUATION) strcat(result, "//");
183             strcat(result, given.host ? given.host : related.host);
184         }
185         
186     if (given.host && related.host)  /* If different hosts, inherit no path. */
187         if (strcmp(given.host, related.host)!=0) {
188             related.absolute=0;
189             related.relative=0;
190             related.anchor=0;
191         }
192         
193     if (wanted & PARSE_PATH) {
194         if(given.absolute) {                            /* All is given */
195             if(wanted & PARSE_PUNCTUATION) strcat(result, "/");
196             strcat(result, given.absolute);
197         } else if(related.absolute) {   /* Adopt path not name */
198             strcat(result, "/");
199             strcat(result, related.absolute);
200             if (given.relative) {
201                 p = strchr(result, '?');        /* Search part? */
202                 if (!p) p=result+strlen(result)-1;
203                 for (; *p!='/'; p--);   /* last / */
204                 p[1]=0;                                 /* Remove filename */
205                 strcat(result, given.relative);         /* Add given one */
206                 HTSimplify (result);
207             }
208         } else if(given.relative) {
209             strcat(result, given.relative);             /* what we've got */
210         } else if(related.relative) {
211             strcat(result, related.relative);
212         } else {  /* No inheritance */
213             strcat(result, "/");
214         }
215     }
216                 
217     if (wanted & PARSE_ANCHOR)
218         if(given.anchor || related.anchor) {
219             if(wanted & PARSE_PUNCTUATION) strcat(result, "#");
220             strcat(result, given.anchor ? given.anchor : related.anchor);
221         }
222     free(rel);
223     free(name);
224     
225     StrAllocCopy(return_value, result);
226     free(result);
227     return return_value;                /* exactly the right length */
228 }
229
230 /*              Simplify a filename
231 //              -------------------
232 //
233 // A unix-style file is allowed to contain the seqeunce xxx/../ which may be
234 // replaced by "" , and the seqeunce "/./" which may be replaced by "/".
235 // Simplification helps us recognize duplicate filenames.
236 //
237 //      Thus,   /etc/junk/../fred       becomes /etc/fred
238 //              /etc/junk/./fred        becomes /etc/junk/fred
239 */
240 #ifdef __STDC__
241 void HTSimplify(char * filename)
242 #else
243 void HTSimplify(filename)
244     char * filename;
245 #endif
246
247 {
248     char * p;
249     char * q;
250     for(p=filename+2; *p; p++) {
251         if (*p=='/') {
252             if ((p[1]=='.') && (p[2]=='.') && (p[3]=='/' || !p[3] )) {
253                 for (q=p-1; (q>filename) && (*q!='/'); q--); /* prev slash */
254                 if (*q=='/') {
255                     strcpy(q, p+3);     /* Remove  /xxx/..      */
256                     if (!*filename) strcpy(filename, "/");
257                     p = q-1;            /* Start again with prev slash  */
258                 } else {                        /*   xxx/..     error?  */
259                     strcpy(filename, p[3] ? p+4 : p+3); /* rm  xxx/../  */
260                     p = filename;               /* Start again */
261                 }
262             } else if ((p[1]=='.') && (p[2]=='/' || !p[2])) {
263                 strcpy(p, p+2);                 /* Remove a slash and a dot */
264             }
265         }
266     }
267 }
268
269
270 /*              Make Relative Name
271 **              ------------------
272 **
273 ** This function creates and returns a string which gives an expression of
274 ** one address as related to another. Where there is no relation, an absolute
275 ** address is retured.
276 **
277 **  On entry,
278 **      Both names must be absolute, fully qualified names of nodes
279 **      (no anchor bits)
280 **
281 **  On exit,
282 **      The return result points to a newly allocated name which, if
283 **      parsed by HTParse relative to relatedName, will yield aName.
284 **      The caller is responsible for freeing the resulting name later.
285 **
286 */
287 #ifdef __STDC__
288 char * HTRelative(const char * aName, const char *relatedName)
289 #else
290 char * HTRelative(aName, relatedName)
291    char * aName;
292    char * relatedName;
293 #endif
294 {
295     char * result = 0;
296     CONST char *p = aName;
297     CONST char *q = relatedName;
298     CONST char * after_access = 0;
299     CONST char * path = 0;
300     CONST char * last_slash = 0;
301     int slashes = 0;
302     
303     for(;*p; p++, q++) {        /* Find extent of match */
304         if (*p!=*q) break;
305         if (*p==':') after_access = p+1;
306         if (*p=='/') {
307             last_slash = p;
308             slashes++;
309             if (slashes==3) path=p;
310         }
311     }
312     
313     /* q, p point to the first non-matching character or zero */
314     
315     if (!after_access) {                        /* Different access */
316         StrAllocCopy(result, aName);
317     } else if (slashes<3){                      /* Different nodes */
318         StrAllocCopy(result, after_access);
319     } else if (slashes==3){                     /* Same node, different path */
320         StrAllocCopy(result, path);
321     } else {                                    /* Some path in common */
322         int levels= 0;
323         for(; *q && (*q!='#'); q++)  if (*q=='/') levels++;
324         result = (char *)malloc(3*levels + strlen(last_slash) + 1);
325       if (result == NULL) outofmem(__FILE__, "HTRelative");
326         result[0]=0;
327         for(;levels; levels--)strcat(result, "../");
328         strcat(result, last_slash+1);
329     }
330     if (TRACE) printf("HT: `%s' expressed relative to\n    `%s' is\n   `%s'.",
331                 aName, relatedName, result);
332     return result;
333 }