comparison mupdf-source/source/xps/xps-util.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2021 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 #include "mupdf/fitz.h"
24 #include "xps-imp.h"
25
26 static inline int xps_tolower(int c)
27 {
28 if (c >= 'A' && c <= 'Z')
29 return c + 32;
30 return c;
31 }
32
33 int
34 xps_strcasecmp(char *a, char *b)
35 {
36 while (xps_tolower(*a) == xps_tolower(*b))
37 {
38 if (*a++ == 0)
39 return 0;
40 b++;
41 }
42 return xps_tolower(*a) - xps_tolower(*b);
43 }
44
45 /* A URL is defined as consisting of a:
46 * SCHEME (e.g. http:)
47 * AUTHORITY (username, password, hostname, port, eg //test:passwd@mupdf.com:999)
48 * PATH (e.g. /download)
49 * QUERY (e.g. ?view=page)
50 * FRAGMENT (e.g. #fred) (not strictly part of the URL)
51 */
52 static char *
53 skip_scheme(char *path)
54 {
55 char *p = path;
56
57 /* Skip over: alpha *(alpha | digit | "+" | "-" | ".") looking for : */
58 if (*p >= 'a' && *p <= 'z')
59 {
60 /* Starts with a-z */
61 }
62 else if (*p >= 'A' && *p <= 'Z')
63 {
64 /* Starts with A-Z */
65 }
66 else
67 return path;
68
69 while (*++p)
70 {
71 if (*p >= 'a' && *p <= 'z')
72 continue;
73 if (*p >= 'A' && *p <= 'Z')
74 continue;
75 if (*p >= '0' && *p <= '9')
76 continue;
77 if (*p == '+')
78 continue;
79 if (*p == '-')
80 continue;
81 if (*p == '.')
82 continue;
83 if (*p == ':')
84 return p+1;
85 break;
86 }
87 return path;
88 }
89
90 static char *
91 skip_authority(char *path)
92 {
93 char *p = path;
94
95 /* Authority section must start with '//' */
96 if (p[0] != '/' || p[1] != '/')
97 return path;
98 p += 2;
99
100 /* Authority is terminated by end of URL, '/' or '?' */
101 while (*p && *p != '/' && *p != '?')
102 p++;
103
104 return p;
105 }
106
107 #define SEP(x) ((x)=='/' || (x) == 0)
108
109 static char *
110 clean_path(char *name)
111 {
112 char *p, *q, *dotdot, *start;
113 int rooted;
114
115 start = skip_scheme(name);
116 start = skip_authority(start);
117 rooted = start[0] == '/';
118
119 /*
120 * invariants:
121 * p points at beginning of path element we're considering.
122 * q points just past the last path element we wrote (no slash).
123 * dotdot points just past the point where .. cannot backtrack
124 * any further (no slash).
125 */
126 p = q = dotdot = start + rooted;
127 while (*p)
128 {
129 if(p[0] == '/') /* null element */
130 p++;
131 else if (p[0] == '.' && SEP(p[1]))
132 p += 1; /* don't count the separator in case it is nul */
133 else if (p[0] == '.' && p[1] == '.' && SEP(p[2]))
134 {
135 p += 2;
136 if (q > dotdot) /* can backtrack */
137 {
138 while(--q > dotdot && *q != '/')
139 ;
140 }
141 else if (!rooted) /* /.. is / but ./../ is .. */
142 {
143 if (q != start)
144 *q++ = '/';
145 *q++ = '.';
146 *q++ = '.';
147 dotdot = q;
148 }
149 }
150 else /* real path element */
151 {
152 if (q != start+rooted)
153 *q++ = '/';
154 while ((*q = *p) != '/' && *q != 0)
155 p++, q++;
156 }
157 }
158
159 /* Protect against 'blah:' input, where start = q = the terminator.
160 * We must not overrun it. */
161 if (q == start && *q != 0) /* empty string is really "." */
162 *q++ = '.';
163 *q = '\0';
164
165 return name;
166 }
167
168 void
169 xps_resolve_url(fz_context *ctx, xps_document *doc, char *output, char *base_uri, char *path, int output_size)
170 {
171 char *p = skip_authority(skip_scheme(path));
172
173 if (p != path || path[0] == '/')
174 {
175 fz_strlcpy(output, path, output_size);
176 }
177 else
178 {
179 size_t len = fz_strlcpy(output, base_uri, output_size);
180 if (len == 0 || output[len-1] != '/')
181 fz_strlcat(output, "/", output_size);
182 fz_strlcat(output, path, output_size);
183 }
184 clean_path(output);
185 }