NetSurf
file.c
Go to the documentation of this file.
1/*
2 * Copyright 2014 vincent Sanders <vince@netsurf-browser.org>
3 *
4 * This file is part of NetSurf, http://www.netsurf-browser.org/
5 *
6 * NetSurf is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * NetSurf is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/** \file
20 * Table operations for files with posix compliant path separator.
21 */
22
23#include <stdarg.h>
24#include <string.h>
25#include <stdio.h>
26#include <sys/types.h>
27#include <sys/stat.h>
28#include <unistd.h>
29#include <fcntl.h>
30#include <errno.h>
31
33
34#include "utils/utils.h"
35#include "utils/corestrings.h"
36#include "utils/url.h"
37#include "utils/nsurl.h"
38#include "utils/string.h"
39#include "utils/file.h"
40#include "utils/dirent.h"
41
42#ifdef nsamiga
44#endif
45
46/**
47 * Generate a posix path from one or more component elemnts.
48 *
49 * If a string is allocated it must be freed by the caller.
50 *
51 * @param[in,out] str pointer to string pointer if this is NULL enough
52 * storage will be allocated for the complete path.
53 * @param[in,out] size The size of the space available if \a str not
54 * NULL on input and if not NULL set to the total
55 * output length on output.
56 * @param[in] nelm The number of elements.
57 * @param[in] ap The elements of the path as string pointers.
58 * @return NSERROR_OK and the complete path is written to str
59 * or error code on faliure.
60 */
61static nserror posix_vmkpath(char **str, size_t *size, size_t nelm, va_list ap)
62{
63 return vsnstrjoin(str, size, '/', nelm, ap);
64}
65
66/**
67 * Get the basename of a file using posix path handling.
68 *
69 * This gets the last element of a path and returns it.
70 *
71 * @param[in] path The path to extract the name from.
72 * @param[in,out] str Pointer to string pointer if this is NULL enough
73 * storage will be allocated for the path element.
74 * @param[in,out] size The size of the space available if \a
75 * str not NULL on input and set to the total
76 * output length on output.
77 * @return NSERROR_OK and the complete path is written to str
78 * or error code on faliure.
79 */
80static nserror posix_basename(const char *path, char **str, size_t *size)
81{
82 const char *leafname;
83 char *fname;
84
85 if (path == NULL) {
87 }
88
89 leafname = strrchr(path, '/');
90 if (!leafname) {
91 leafname = path;
92 } else {
93 leafname += 1;
94 }
95
96 fname = strdup(leafname);
97 if (fname == NULL) {
98 return NSERROR_NOMEM;
99 }
100
101 *str = fname;
102 if (size != NULL) {
103 *size = strlen(fname);
104 }
105 return NSERROR_OK;
106}
107
108/**
109 * Create a path from a nsurl using posix file handling.
110 *
111 * @param[in] url The url to encode.
112 * @param[out] path_out A string containing the result path which should
113 * be freed by the caller.
114 * @return NSERROR_OK and the path is written to \a path or error code
115 * on faliure.
116 */
117static nserror posix_nsurl_to_path(struct nsurl *url, char **path_out)
118{
119 lwc_string *urlpath;
120 char *path;
121 bool match;
122 lwc_string *scheme;
123 nserror res;
124
125 if ((url == NULL) || (path_out == NULL)) {
127 }
128
129 scheme = nsurl_get_component(url, NSURL_SCHEME);
130
131 if (lwc_string_caseless_isequal(scheme, corestring_lwc_file,
132 &match) != lwc_error_ok)
133 {
135 }
136 lwc_string_unref(scheme);
137 if (match == false) {
139 }
140
141 urlpath = nsurl_get_component(url, NSURL_PATH);
142 if (urlpath == NULL) {
144 }
145
146 res = url_unescape(lwc_string_data(urlpath),
147 lwc_string_length(urlpath),
148 NULL,
149 &path);
150 lwc_string_unref(urlpath);
151 if (res != NSERROR_OK) {
152 return res;
153 }
154
155 *path_out = path;
156
157 return NSERROR_OK;
158}
159
160/**
161 * Create a nsurl from a path using posix file handling.
162 *
163 * Perform the necessary operations on a path to generate a nsurl.
164 *
165 * @param[in] path The path to convert.
166 * @param[out] url_out pointer to recive the nsurl, The returned url
167 * should be unreferenced by the caller.
168 * @return NSERROR_OK and the url is placed in \a url or error code on
169 * faliure.
170 */
171static nserror posix_path_to_nsurl(const char *path, struct nsurl **url_out)
172{
173 nserror ret;
174 int urllen;
175 char *urlstr;
176 char *escpath; /* escaped version of the path */
177 char *escpaths;
178
179 if ((path == NULL) || (url_out == NULL) || (*path == 0)) {
181 }
182
183 /* escape the path so it can be placed in a url */
184 ret = url_escape(path, false, "/", &escpath);
185 if (ret != NSERROR_OK) {
186 return ret;
187 }
188 /* remove unecessary / as file: paths are already absolute */
189 escpaths = escpath;
190 while (*escpaths == '/') {
191 escpaths++;
192 }
193
194 /* build url as a string for nsurl constructor */
195 urllen = strlen(escpaths) + FILE_SCHEME_PREFIX_LEN + 1;
196 urlstr = malloc(urllen);
197 if (urlstr == NULL) {
198 free(escpath);
199 return NSERROR_NOMEM;
200 }
201
202 snprintf(urlstr, urllen, "%s%s", FILE_SCHEME_PREFIX, escpaths);
203 free(escpath);
204
205 ret = nsurl_create(urlstr, url_out);
206 free(urlstr);
207
208 return ret;
209}
210
211/**
212 * Ensure that all directory elements needed to store a filename exist.
213 *
214 * @param fname The filename to ensure the path to exists.
215 * @return NSERROR_OK on success or error code on failure.
216 */
217static nserror posix_mkdir_all(const char *fname)
218{
219 char *dname;
220 char *sep;
221 struct stat sb;
222
223 dname = strdup(fname);
224
225 sep = strrchr(dname, '/');
226 if (sep == NULL) {
227 /* no directory separator path is just filename so its ok */
228 free(dname);
229 return NSERROR_OK;
230 }
231
232 *sep = 0; /* null terminate directory path */
233
234 if (stat(dname, &sb) == 0) {
235 free(dname);
236 if (S_ISDIR(sb.st_mode)) {
237 /* path to file exists and is a directory */
238 return NSERROR_OK;
239 }
241 }
242 *sep = '/'; /* restore separator */
243
244 sep = dname;
245 while (*sep == '/') {
246 sep++;
247 }
248 while ((sep = strchr(sep, '/')) != NULL) {
249 *sep = 0;
250 if (stat(dname, &sb) != 0) {
251 if (nsmkdir(dname, S_IRWXU) != 0) {
252 /* could not create path element */
253 free(dname);
254 return NSERROR_NOT_FOUND;
255 }
256 } else {
257 if (! S_ISDIR(sb.st_mode)) {
258 /* path element not a directory */
259 free(dname);
261 }
262 }
263 *sep = '/'; /* restore separator */
264 /* skip directory separators */
265 while (*sep == '/') {
266 sep++;
267 }
268 }
269
270 free(dname);
271 return NSERROR_OK;
272}
273
274/**
275 * default to using the posix file handling
276 */
277static struct gui_file_table file_table = {
279 .basename = posix_basename,
280 .nsurl_to_path = posix_nsurl_to_path,
281 .path_to_nsurl = posix_path_to_nsurl,
282 .mkdir_all = posix_mkdir_all,
283};
284
286
287/* exported interface documented in utils/file.h */
288nserror netsurf_mkpath(char **str, size_t *size, size_t nelm, ...)
289{
290 va_list ap;
291 nserror ret;
292
293 va_start(ap, nelm);
294 ret = guit->file->mkpath(str, size, nelm, ap);
295 va_end(ap);
296
297 return ret;
298}
299
300/* exported interface documented in utils/file.h */
301nserror netsurf_nsurl_to_path(struct nsurl *url, char **path_out)
302{
303 return guit->file->nsurl_to_path(url, path_out);
304}
305
306/* exported interface documented in utils/file.h */
307nserror netsurf_path_to_nsurl(const char *path, struct nsurl **url)
308{
309 return guit->file->path_to_nsurl(path, url);
310}
311
312/* exported interface documented in utils/file.h */
313nserror netsurf_mkdir_all(const char *fname)
314{
315 return guit->file->mkdir_all(fname);
316}
317
318/* exported interface documented in utils/file.h */
321{
322 DIR *parent;
323 struct dirent *entry;
324 nserror ret = NSERROR_OK;
325 struct stat ent_stat; /* stat result of leaf entry */
326
327 parent = opendir(path);
328 if (parent == NULL) {
329 switch (errno) {
330 case ENOENT:
331 return NSERROR_NOT_FOUND;
332 default:
333 return NSERROR_UNKNOWN;
334 }
335 }
336
337 while ((entry = readdir(parent))) {
338 char *leafpath = NULL;
339
340 if (strcmp(entry->d_name, ".") == 0 ||
341 strcmp(entry->d_name, "..") == 0)
342 continue;
343
344 ret = netsurf_mkpath(&leafpath, NULL, 2, path, entry->d_name);
345 if (ret != NSERROR_OK)
346 goto out;
347
348#if (defined(HAVE_DIRFD) && defined(HAVE_FSTATAT))
349 if (fstatat(dirfd(parent), entry->d_name, &ent_stat,
350 AT_SYMLINK_NOFOLLOW) != 0) {
351#else
352 if (stat(leafpath, &ent_stat) != 0) {
353#endif
354 free(leafpath);
355 goto out_via_errno;
356 }
357 if (S_ISDIR(ent_stat.st_mode)) {
358 ret = netsurf_recursive_rm(leafpath);
359 if (ret != NSERROR_OK) {
360 free(leafpath);
361 goto out;
362 }
363 } else {
364#if (defined(HAVE_DIRFD) && defined(HAVE_UNLINKAT))
365 if (unlinkat(dirfd(parent), entry->d_name, 0) != 0) {
366#else
367 if (unlink(leafpath) != 0) {
368#endif
369 free(leafpath);
370 goto out_via_errno;
371 }
372 }
373
374 free(leafpath);
375 }
376
377 if (rmdir(path) != 0) {
378 goto out_via_errno;
379 }
380
381 goto out;
382
383out_via_errno:
384 switch (errno) {
385 case ENOENT:
386 ret = NSERROR_NOT_FOUND;
387 break;
388 default:
389 ret = NSERROR_UNKNOWN;
390 }
391out:
392 closedir(parent);
393
394 return ret;
395}
Useful interned string pointers (interface).
#define FILE_SCHEME_PREFIX_LEN
File url prefix length.
Definition: corestrings.h:33
#define FILE_SCHEME_PREFIX
File url prefix.
Definition: corestrings.h:30
wimp_w parent
Definition: dialog.c:88
directory traversal and entry
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_NOT_FOUND
Requested item not found.
Definition: errors.h:34
@ NSERROR_BAD_PARAMETER
Bad Parameter.
Definition: errors.h:48
@ NSERROR_UNKNOWN
Unknown error - DO NOT USE.
Definition: errors.h:31
@ NSERROR_NOT_DIRECTORY
Missing directory.
Definition: errors.h:35
@ NSERROR_NOMEM
Memory exhaustion.
Definition: errors.h:32
@ NSERROR_OK
No error.
Definition: errors.h:30
struct netsurf_table * guit
The global interface table.
Definition: gui_factory.c:49
Interface to core interface table.
NetSurf URL handling (interface).
nserror nsurl_create(const char *const url_s, nsurl **url)
Create a NetSurf URL object from a URL string.
lwc_string * nsurl_get_component(const nsurl *url, nsurl_component part)
Get part of a URL as a lwc_string, from a NetSurf URL object.
@ NSURL_SCHEME
Definition: nsurl.h:45
@ NSURL_PATH
Definition: nsurl.h:52
struct nsurl nsurl
NetSurf URL object.
Definition: nsurl.h:31
Minimal compatibility header for AmigaOS 3.
Interface to utility string handling.
nserror vsnstrjoin(char **str, size_t *size, char sep, size_t nelm, va_list ap)
Generate a string from one or more component elements separated with a single value.
Definition: utils.c:107
/brief function table for file and filename operations.
Definition: file.h:50
nserror(* mkdir_all)(const char *fname)
Ensure that all directory elements needed to store a filename exist.
Definition: file.h:116
nserror(* nsurl_to_path)(struct nsurl *url, char **path)
Create a path from a nsurl.
Definition: file.h:95
nserror(* mkpath)(char **str, size_t *size, size_t nemb, va_list ap)
Generate a path from one or more component elemnts.
Definition: file.h:68
nserror(* path_to_nsurl)(const char *path, struct nsurl **url)
Create a nsurl from a path.
Definition: file.h:108
struct gui_file_table * file
File table.
Definition: gui_table.h:95
nserror url_escape(const char *unescaped, bool sptoplus, const char *escexceptions, char **result)
Escape a string suitable for inclusion in an URL.
Definition: url.c:131
nserror url_unescape(const char *str, size_t length, size_t *length_out, char **result_out)
Convert an escaped string to plain.
Definition: url.c:67
Interface to URL parsing and joining operations.
static nserror posix_nsurl_to_path(struct nsurl *url, char **path_out)
Create a path from a nsurl using posix file handling.
Definition: file.c:117
nserror netsurf_mkdir_all(const char *fname)
Ensure that all directory elements needed to store a filename exist.
Definition: file.c:313
static nserror posix_path_to_nsurl(const char *path, struct nsurl **url_out)
Create a nsurl from a path using posix file handling.
Definition: file.c:171
nserror netsurf_nsurl_to_path(struct nsurl *url, char **path_out)
Create a path from a nsurl.
Definition: file.c:301
static nserror posix_mkdir_all(const char *fname)
Ensure that all directory elements needed to store a filename exist.
Definition: file.c:217
static struct gui_file_table file_table
default to using the posix file handling
Definition: file.c:277
struct gui_file_table * default_file_table
Default (posix) file operation table.
Definition: file.c:285
static nserror posix_basename(const char *path, char **str, size_t *size)
Get the basename of a file using posix path handling.
Definition: file.c:80
static nserror posix_vmkpath(char **str, size_t *size, size_t nelm, va_list ap)
Generate a posix path from one or more component elemnts.
Definition: file.c:61
nserror netsurf_path_to_nsurl(const char *path, struct nsurl **url)
Create a nsurl from a path.
Definition: file.c:307
nserror netsurf_mkpath(char **str, size_t *size, size_t nelm,...)
Generate a path from one or more component elemnts.
Definition: file.c:288
nserror netsurf_recursive_rm(const char *path)
Recursively remove a directory.
Definition: file.c:320
Default operations table for files.
Interface to a number of general purpose functionality.
#define nsmkdir(dir, mode)
POSIX mkdir function.
Definition: utils.h:60
static nserror path(const struct redraw_context *ctx, const plot_style_t *pstyle, const float *p, unsigned int n, const float transform[6])
Plots a path.
Definition: plot.c:821