1#include "mupdf/fitz.h"
2#include "xps-imp.h"
3
4static inline int xps_tolower(int c)
5{
6 if (c >= 'A' && c <= 'Z')
7 return c + 32;
8 return c;
9}
10
11int
12xps_strcasecmp(char *a, char *b)
13{
14 while (xps_tolower(*a) == xps_tolower(*b))
15 {
16 if (*a++ == 0)
17 return 0;
18 b++;
19 }
20 return xps_tolower(*a) - xps_tolower(*b);
21}
22
23/* A URL is defined as consisting of a:
24 * SCHEME (e.g. http:)
25 * AUTHORITY (username, password, hostname, port, eg //test:passwd@mupdf.com:999)
26 * PATH (e.g. /download)
27 * QUERY (e.g. ?view=page)
28 * FRAGMENT (e.g. #fred) (not strictly part of the URL)
29 */
30static char *
31skip_scheme(char *path)
32{
33 char *p = path;
34
35 /* Skip over: alpha *(alpha | digit | "+" | "-" | ".") looking for : */
36 if (*p >= 'a' && *p <= 'z')
37 {}
38 else if (*p >= 'A' && *p <= 'Z')
39 {}
40 else
41 return path;
42
43 while (*++p)
44 {
45 if (*p >= 'a' && *p <= 'z')
46 {}
47 else if (*p >= 'A' && *p <= 'Z')
48 {}
49 else if (*p >= '0' && *p <= '9')
50 {}
51 else if (*p == '+')
52 {}
53 else if (*p == '-')
54 {}
55 else if (*p == '.')
56 {}
57 else if (*p == ':')
58 return p+1;
59 else
60 break;
61 }
62 return path;
63}
64
65static char *
66skip_authority(char *path)
67{
68 char *p = path;
69
70 /* Authority section must start with '//' */
71 if (p[0] != '/' || p[1] != '/')
72 return path;
73 p += 2;
74
75 /* Authority is terminated by end of URL, '/' or '?' */
76 while (*p && *p != '/' && *p != '?')
77 p++;
78
79 return p;
80}
81
82#define SEP(x) ((x)=='/' || (x) == 0)
83
84static char *
85clean_path(char *name)
86{
87 char *p, *q, *dotdot, *start;
88 int rooted;
89
90 start = skip_scheme(name);
91 start = skip_authority(start);
92 rooted = start[0] == '/';
93
94 /*
95 * invariants:
96 * p points at beginning of path element we're considering.
97 * q points just past the last path element we wrote (no slash).
98 * dotdot points just past the point where .. cannot backtrack
99 * any further (no slash).
100 */
101 p = q = dotdot = start + rooted;
102 while (*p)
103 {
104 if(p[0] == '/') /* null element */
105 p++;
106 else if (p[0] == '.' && SEP(p[1]))
107 p += 1; /* don't count the separator in case it is nul */
108 else if (p[0] == '.' && p[1] == '.' && SEP(p[2]))
109 {
110 p += 2;
111 if (q > dotdot) /* can backtrack */
112 {
113 while(--q > dotdot && *q != '/')
114 ;
115 }
116 else if (!rooted) /* /.. is / but ./../ is .. */
117 {
118 if (q != start)
119 *q++ = '/';
120 *q++ = '.';
121 *q++ = '.';
122 dotdot = q;
123 }
124 }
125 else /* real path element */
126 {
127 if (q != start+rooted)
128 *q++ = '/';
129 while ((*q = *p) != '/' && *q != 0)
130 p++, q++;
131 }
132 }
133
134 if (q == start) /* empty string is really "." */
135 *q++ = '.';
136 *q = '\0';
137
138 return name;
139}
140
141void
142xps_resolve_url(fz_context *ctx, xps_document *doc, char *output, char *base_uri, char *path, int output_size)
143{
144 char *p = skip_authority(skip_scheme(path));
145
146 if (p != path || path[0] == '/')
147 {
148 fz_strlcpy(output, path, output_size);
149 }
150 else
151 {
152 size_t len = fz_strlcpy(output, base_uri, output_size);
153 if (len == 0 || output[len-1] != '/')
154 fz_strlcat(output, "/", output_size);
155 fz_strlcat(output, path, output_size);
156 }
157 clean_path(output);
158}
159