1 | #include "mupdf/fitz.h" |
2 | #include "xps-imp.h" |
3 | |
4 | static inline int xps_tolower(int c) |
5 | { |
6 | if (c >= 'A' && c <= 'Z') |
7 | return c + 32; |
8 | return c; |
9 | } |
10 | |
11 | int |
12 | xps_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 | */ |
30 | static char * |
31 | skip_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 | |
65 | static char * |
66 | skip_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 | |
84 | static char * |
85 | clean_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 | |
141 | void |
142 | xps_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 | |