wmaker: replaced macro by an inline function, in X Modifier initialisation
[wmaker-crm.git] / WINGs / findfile.c
blobb5f1b1f02627f9d84979fe2212d609d4eb995440
1 /*
2 * Window Maker miscelaneous function library
4 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * This program 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; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
22 #include "wconfig.h"
24 #include "WUtil.h"
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <errno.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <pwd.h>
35 #include <limits.h>
37 #ifndef PATH_MAX
38 #define PATH_MAX 1024
39 #endif
42 char *wgethomedir()
44 static char *home = NULL;
45 char *tmp;
46 struct passwd *user;
48 if (home)
49 return home;
51 #ifdef HAVE_SECURE_GETENV
52 tmp = secure_getenv("HOME");
53 #else
54 tmp = getenv("HOME");
55 #endif
56 if (tmp) {
57 home = wstrdup(tmp);
58 return home;
61 user = getpwuid(getuid());
62 if (!user) {
63 werror(_("could not get password entry for UID %i"), getuid());
64 home = "/";
65 return home;
68 if (!user->pw_dir)
69 home = "/";
70 else
71 home = wstrdup(user->pw_dir);
73 return home;
77 * Return the home directory for the specified used
79 * If user not found, returns NULL, otherwise always returns a path that is
80 * statically stored.
82 * Please note you must use the path before any other call to 'getpw*' or it
83 * may be erased. This is a design choice to avoid duplication considering
84 * the use case for this function.
86 static const char *getuserhomedir(const char *username)
88 static const char default_home[] = "/";
89 struct passwd *user;
91 user = getpwnam(username);
92 if (!user) {
93 werror(_("could not get password entry for user %s"), username);
94 return NULL;
96 if (!user->pw_dir)
97 return default_home;
98 else
99 return user->pw_dir;
103 char *wexpandpath(const char *path)
105 const char *origpath = path;
106 char buffer2[PATH_MAX + 2];
107 char buffer[PATH_MAX + 2];
108 int i;
110 memset(buffer, 0, PATH_MAX + 2);
112 if (*path == '~') {
113 const char *home;
115 path++;
116 if (*path == '/' || *path == 0) {
117 home = wgethomedir();
118 if (strlen(home) > PATH_MAX ||
119 wstrlcpy(buffer, home, sizeof(buffer)) >= sizeof(buffer))
120 goto error;
121 } else {
122 int j;
123 j = 0;
124 while (*path != 0 && *path != '/') {
125 if (j > PATH_MAX)
126 goto error;
127 buffer2[j++] = *path;
128 buffer2[j] = 0;
129 path++;
131 home = getuserhomedir(buffer2);
132 if (!home || wstrlcat(buffer, home, sizeof(buffer)) >= sizeof(buffer))
133 goto error;
137 i = strlen(buffer);
139 while (*path != 0 && i <= PATH_MAX) {
140 char *tmp;
142 if (*path == '$') {
143 int j;
145 path++;
146 /* expand $(HOME) or $HOME style environment variables */
147 if (*path == '(') {
148 path++;
149 j = 0;
150 while (*path != 0 && *path != ')') {
151 if (j > PATH_MAX)
152 goto error;
153 buffer2[j++] = *(path++);
155 buffer2[j] = 0;
156 if (*path == ')') {
157 path++;
158 tmp = getenv(buffer2);
159 } else {
160 tmp = NULL;
162 if (!tmp) {
163 if ((i += strlen(buffer2) + 2) > PATH_MAX)
164 goto error;
165 buffer[i] = 0;
166 if (wstrlcat(buffer, "$(", sizeof(buffer)) >= sizeof(buffer) ||
167 wstrlcat(buffer, buffer2, sizeof(buffer)) >= sizeof(buffer))
168 goto error;
169 if (*(path-1)==')') {
170 if (++i > PATH_MAX ||
171 wstrlcat(buffer, ")", sizeof(buffer)) >= sizeof(buffer))
172 goto error;
174 } else {
175 if ((i += strlen(tmp)) > PATH_MAX ||
176 wstrlcat(buffer, tmp, sizeof(buffer)) >= sizeof(buffer))
177 goto error;
179 } else {
180 j = 0;
181 while (*path != 0 && *path != '/') {
182 if (j > PATH_MAX)
183 goto error;
184 buffer2[j++] = *(path++);
186 buffer2[j] = 0;
187 tmp = getenv(buffer2);
188 if (!tmp) {
189 if ((i += strlen(buffer2) + 1) > PATH_MAX ||
190 wstrlcat(buffer, "$", sizeof(buffer)) >= sizeof(buffer) ||
191 wstrlcat(buffer, buffer2, sizeof(buffer)) >= sizeof(buffer))
192 goto error;
193 } else {
194 if ((i += strlen(tmp)) > PATH_MAX ||
195 wstrlcat(buffer, tmp, sizeof(buffer)) >= sizeof(buffer))
196 goto error;
199 } else {
200 buffer[i++] = *path;
201 path++;
205 if (*path!=0)
206 goto error;
208 return wstrdup(buffer);
210 error:
211 errno = ENAMETOOLONG;
212 werror(_("could not expand %s"), origpath);
214 return NULL;
217 /* return address of next char != tok or end of string whichever comes first */
218 static const char *skipchar(const char *string, char tok)
220 while (*string != 0 && *string == tok)
221 string++;
223 return string;
226 /* return address of next char == tok or end of string whichever comes first */
227 static const char *nextchar(const char *string, char tok)
229 while (*string != 0 && *string != tok)
230 string++;
232 return string;
236 *----------------------------------------------------------------------
237 * findfile--
238 * Finds a file in a : separated list of paths. ~ expansion is also
239 * done.
241 * Returns:
242 * The complete path for the file (in a newly allocated string) or
243 * NULL if the file was not found.
245 * Side effects:
246 * A new string is allocated. It must be freed later.
248 *----------------------------------------------------------------------
250 char *wfindfile(const char *paths, const char *file)
252 char *path;
253 const char *tmp, *tmp2;
254 int len, flen;
255 char *fullpath;
257 if (!file)
258 return NULL;
260 if (*file == '/' || *file == '~' || *file == '$' || !paths || *paths == 0) {
261 if (access(file, F_OK) < 0) {
262 fullpath = wexpandpath(file);
263 if (!fullpath)
264 return NULL;
266 if (access(fullpath, F_OK) < 0) {
267 wfree(fullpath);
268 return NULL;
269 } else {
270 return fullpath;
272 } else {
273 return wstrdup(file);
277 flen = strlen(file);
278 tmp = paths;
279 while (*tmp) {
280 tmp = skipchar(tmp, ':');
281 if (*tmp == 0)
282 break;
283 tmp2 = nextchar(tmp, ':');
284 len = tmp2 - tmp;
285 path = wmalloc(len + flen + 2);
286 path = memcpy(path, tmp, len);
287 path[len] = 0;
288 if (path[len - 1] != '/' &&
289 wstrlcat(path, "/", len + flen + 2) >= len + flen + 2) {
290 wfree(path);
291 return NULL;
294 if (wstrlcat(path, file, len + flen + 2) >= len + flen + 2) {
295 wfree(path);
296 return NULL;
299 fullpath = wexpandpath(path);
300 wfree(path);
302 if (fullpath) {
303 if (access(fullpath, F_OK) == 0) {
304 return fullpath;
306 wfree(fullpath);
308 tmp = tmp2;
311 return NULL;
314 char *wfindfileinlist(char *const *path_list, const char *file)
316 int i;
317 char *path;
318 int len, flen;
319 char *fullpath;
321 if (!file)
322 return NULL;
324 if (*file == '/' || *file == '~' || !path_list) {
325 if (access(file, F_OK) < 0) {
326 fullpath = wexpandpath(file);
327 if (!fullpath)
328 return NULL;
330 if (access(fullpath, F_OK) < 0) {
331 wfree(fullpath);
332 return NULL;
333 } else {
334 return fullpath;
336 } else {
337 return wstrdup(file);
341 flen = strlen(file);
342 for (i = 0; path_list[i] != NULL; i++) {
343 len = strlen(path_list[i]);
344 path = wmalloc(len + flen + 2);
345 path = memcpy(path, path_list[i], len);
346 path[len] = 0;
347 if (wstrlcat(path, "/", len + flen + 2) >= len + flen + 2 ||
348 wstrlcat(path, file, len + flen + 2) >= len + flen + 2) {
349 wfree(path);
350 return NULL;
352 /* expand tilde */
353 fullpath = wexpandpath(path);
354 wfree(path);
355 if (fullpath) {
356 /* check if file exists */
357 if (access(fullpath, F_OK) == 0) {
358 return fullpath;
360 wfree(fullpath);
364 return NULL;
367 char *wfindfileinarray(WMPropList *array, const char *file)
369 int i;
370 char *path;
371 int len, flen;
372 char *fullpath;
374 if (!file)
375 return NULL;
377 if (*file == '/' || *file == '~' || !array) {
378 if (access(file, F_OK) < 0) {
379 fullpath = wexpandpath(file);
380 if (!fullpath)
381 return NULL;
383 if (access(fullpath, F_OK) < 0) {
384 wfree(fullpath);
385 return NULL;
386 } else {
387 return fullpath;
389 } else {
390 return wstrdup(file);
394 flen = strlen(file);
395 for (i = 0; i < WMGetPropListItemCount(array); i++) {
396 WMPropList *prop;
397 char *p;
399 prop = WMGetFromPLArray(array, i);
400 if (!prop)
401 continue;
402 p = WMGetFromPLString(prop);
404 len = strlen(p);
405 path = wmalloc(len + flen + 2);
406 path = memcpy(path, p, len);
407 path[len] = 0;
408 if (wstrlcat(path, "/", len + flen + 2) >= len + flen + 2 ||
409 wstrlcat(path, file, len + flen + 2) >= len + flen + 2) {
410 wfree(path);
411 return NULL;
413 /* expand tilde */
414 fullpath = wexpandpath(path);
415 wfree(path);
416 if (fullpath) {
417 /* check if file exists */
418 if (access(fullpath, F_OK) == 0) {
419 return fullpath;
421 wfree(fullpath);
424 return NULL;
427 int wcopy_file(const char *dest_dir, const char *src_file, const char *dest_file)
429 char *path_dst;
430 int fd_src, fd_dst;
431 struct stat stat_src;
432 mode_t permission_dst;
433 const size_t buffer_size = 2 * 1024 * 1024; /* 4MB is a decent start choice to allow the OS to take advantage of modern disk's performance */
434 char *buffer; /* The buffer is not created on the stack to avoid possible stack overflow as our buffer is big */
436 try_again_src:
437 fd_src = open(src_file, O_RDONLY | O_NOFOLLOW);
438 if (fd_src == -1) {
439 if (errno == EINTR)
440 goto try_again_src;
441 werror(_("Could not open input file \"%s\": %s"), src_file, strerror(errno));
442 return -1;
445 /* Only accept to copy regular files */
446 if (fstat(fd_src, &stat_src) != 0 || !S_ISREG(stat_src.st_mode)) {
447 close(fd_src);
448 return -1;
451 path_dst = wstrconcat(dest_dir, dest_file);
452 try_again_dst:
453 fd_dst = open(path_dst, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
454 if (fd_dst == -1) {
455 if (errno == EINTR)
456 goto try_again_dst;
457 werror(_("Could not create target file \"%s\": %s"), path_dst, strerror(errno));
458 wfree(path_dst);
459 close(fd_src);
460 return -1;
463 buffer = malloc(buffer_size); /* Don't use wmalloc to avoid the memset(0) we don't need */
464 if (buffer == NULL) {
465 werror(_("could not allocate memory for the copy buffer"));
466 close(fd_dst);
467 goto cleanup_and_return_failure;
470 for (;;) {
471 ssize_t size_data;
472 const char *write_ptr;
473 size_t write_remain;
475 try_again_read:
476 size_data = read(fd_src, buffer, buffer_size);
477 if (size_data == 0)
478 break; /* End of File have been reached */
479 if (size_data < 0) {
480 if (errno == EINTR)
481 goto try_again_read;
482 werror(_("could not read from file \"%s\": %s"), src_file, strerror(errno));
483 close(fd_dst);
484 goto cleanup_and_return_failure;
487 write_ptr = buffer;
488 write_remain = size_data;
489 while (write_remain > 0) {
490 ssize_t write_done;
492 try_again_write:
493 write_done = write(fd_dst, write_ptr, write_remain);
494 if (write_done < 0) {
495 if (errno == EINTR)
496 goto try_again_write;
497 werror(_("could not write data to file \"%s\": %s"), path_dst, strerror(errno));
498 close(fd_dst);
499 goto cleanup_and_return_failure;
501 write_ptr += write_done;
502 write_remain -= write_done;
506 /* Keep only the permission-related part of the field: */
507 permission_dst = stat_src.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX);
508 if (fchmod(fd_dst, permission_dst) != 0)
509 wwarning(_("could not set permission 0%03o on file \"%s\": %s"),
510 permission_dst, path_dst, strerror(errno));
512 if (close(fd_dst) != 0) {
513 werror(_("could not close the file \"%s\": %s"), path_dst, strerror(errno));
514 cleanup_and_return_failure:
515 free(buffer);
516 wfree(path_dst);
517 close(fd_src);
518 unlink(path_dst);
519 return -1;
522 free(buffer);
523 wfree(path_dst);
524 close(fd_src);
526 return 0;