Include screen.h in dialog.h for definition of WScreen
[wmaker-crm.git] / WINGs / findfile.c
blobbb2ce7f0cf1a5fc6f5d101a4466260f6b92b7642
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 const char *wgethomedir(void)
44 static char *home = NULL;
45 char *tmp;
46 struct passwd *user;
48 if (home)
49 return home;
51 tmp = GETENV("HOME");
52 if (tmp) {
53 home = wstrdup(tmp);
54 return home;
57 user = getpwuid(getuid());
58 if (!user) {
59 werror(_("could not get password entry for UID %i"), getuid());
60 home = "/";
61 return home;
64 if (!user->pw_dir)
65 home = "/";
66 else
67 home = wstrdup(user->pw_dir);
69 return home;
73 * Return the home directory for the specified used
75 * If user not found, returns NULL, otherwise always returns a path that is
76 * statically stored.
78 * Please note you must use the path before any other call to 'getpw*' or it
79 * may be erased. This is a design choice to avoid duplication considering
80 * the use case for this function.
82 static const char *getuserhomedir(const char *username)
84 static const char default_home[] = "/";
85 struct passwd *user;
87 user = getpwnam(username);
88 if (!user) {
89 werror(_("could not get password entry for user %s"), username);
90 return NULL;
92 if (!user->pw_dir)
93 return default_home;
94 else
95 return user->pw_dir;
99 char *wexpandpath(const char *path)
101 const char *origpath = path;
102 char buffer2[PATH_MAX + 2];
103 char buffer[PATH_MAX + 2];
104 int i;
106 memset(buffer, 0, PATH_MAX + 2);
108 if (*path == '~') {
109 const char *home;
111 path++;
112 if (*path == '/' || *path == 0) {
113 home = wgethomedir();
114 if (strlen(home) > PATH_MAX ||
115 wstrlcpy(buffer, home, sizeof(buffer)) >= sizeof(buffer))
116 goto error;
117 } else {
118 int j;
119 j = 0;
120 while (*path != 0 && *path != '/') {
121 if (j > PATH_MAX)
122 goto error;
123 buffer2[j++] = *path;
124 buffer2[j] = 0;
125 path++;
127 home = getuserhomedir(buffer2);
128 if (!home || wstrlcat(buffer, home, sizeof(buffer)) >= sizeof(buffer))
129 goto error;
133 i = strlen(buffer);
135 while (*path != 0 && i <= PATH_MAX) {
136 char *tmp;
138 if (*path == '$') {
139 int j;
141 path++;
142 /* expand $(HOME) or $HOME style environment variables */
143 if (*path == '(') {
144 path++;
145 j = 0;
146 while (*path != 0 && *path != ')') {
147 if (j > PATH_MAX)
148 goto error;
149 buffer2[j++] = *(path++);
151 buffer2[j] = 0;
152 if (*path == ')') {
153 path++;
154 tmp = getenv(buffer2);
155 } else {
156 tmp = NULL;
158 if (!tmp) {
159 if ((i += strlen(buffer2) + 2) > PATH_MAX)
160 goto error;
161 buffer[i] = 0;
162 if (wstrlcat(buffer, "$(", sizeof(buffer)) >= sizeof(buffer) ||
163 wstrlcat(buffer, buffer2, sizeof(buffer)) >= sizeof(buffer))
164 goto error;
165 if (*(path-1)==')') {
166 if (++i > PATH_MAX ||
167 wstrlcat(buffer, ")", sizeof(buffer)) >= sizeof(buffer))
168 goto error;
170 } else {
171 if ((i += strlen(tmp)) > PATH_MAX ||
172 wstrlcat(buffer, tmp, sizeof(buffer)) >= sizeof(buffer))
173 goto error;
175 } else {
176 j = 0;
177 while (*path != 0 && *path != '/') {
178 if (j > PATH_MAX)
179 goto error;
180 buffer2[j++] = *(path++);
182 buffer2[j] = 0;
183 tmp = getenv(buffer2);
184 if (!tmp) {
185 if ((i += strlen(buffer2) + 1) > PATH_MAX ||
186 wstrlcat(buffer, "$", sizeof(buffer)) >= sizeof(buffer) ||
187 wstrlcat(buffer, buffer2, sizeof(buffer)) >= sizeof(buffer))
188 goto error;
189 } else {
190 if ((i += strlen(tmp)) > PATH_MAX ||
191 wstrlcat(buffer, tmp, sizeof(buffer)) >= sizeof(buffer))
192 goto error;
195 } else {
196 buffer[i++] = *path;
197 path++;
201 if (*path!=0)
202 goto error;
204 return wstrdup(buffer);
206 error:
207 errno = ENAMETOOLONG;
208 werror(_("could not expand %s"), origpath);
210 return NULL;
213 /* return address of next char != tok or end of string whichever comes first */
214 static const char *skipchar(const char *string, char tok)
216 while (*string != 0 && *string == tok)
217 string++;
219 return string;
222 /* return address of next char == tok or end of string whichever comes first */
223 static const char *nextchar(const char *string, char tok)
225 while (*string != 0 && *string != tok)
226 string++;
228 return string;
232 *----------------------------------------------------------------------
233 * findfile--
234 * Finds a file in a : separated list of paths. ~ expansion is also
235 * done.
237 * Returns:
238 * The complete path for the file (in a newly allocated string) or
239 * NULL if the file was not found.
241 * Side effects:
242 * A new string is allocated. It must be freed later.
244 *----------------------------------------------------------------------
246 char *wfindfile(const char *paths, const char *file)
248 char *path;
249 const char *tmp, *tmp2;
250 int len, flen;
251 char *fullpath;
253 if (!file)
254 return NULL;
256 if (*file == '/' || *file == '~' || *file == '$' || !paths || *paths == 0) {
257 if (access(file, F_OK) < 0) {
258 fullpath = wexpandpath(file);
259 if (!fullpath)
260 return NULL;
262 if (access(fullpath, F_OK) < 0) {
263 wfree(fullpath);
264 return NULL;
265 } else {
266 return fullpath;
268 } else {
269 return wstrdup(file);
273 flen = strlen(file);
274 tmp = paths;
275 while (*tmp) {
276 tmp = skipchar(tmp, ':');
277 if (*tmp == 0)
278 break;
279 tmp2 = nextchar(tmp, ':');
280 len = tmp2 - tmp;
281 path = wmalloc(len + flen + 2);
282 path = memcpy(path, tmp, len);
283 path[len] = 0;
284 if (path[len - 1] != '/' &&
285 wstrlcat(path, "/", len + flen + 2) >= len + flen + 2) {
286 wfree(path);
287 return NULL;
290 if (wstrlcat(path, file, len + flen + 2) >= len + flen + 2) {
291 wfree(path);
292 return NULL;
295 fullpath = wexpandpath(path);
296 wfree(path);
298 if (fullpath) {
299 if (access(fullpath, F_OK) == 0) {
300 return fullpath;
302 wfree(fullpath);
304 tmp = tmp2;
307 return NULL;
310 char *wfindfileinlist(char *const *path_list, const char *file)
312 int i;
313 char *path;
314 int len, flen;
315 char *fullpath;
317 if (!file)
318 return NULL;
320 if (*file == '/' || *file == '~' || !path_list) {
321 if (access(file, F_OK) < 0) {
322 fullpath = wexpandpath(file);
323 if (!fullpath)
324 return NULL;
326 if (access(fullpath, F_OK) < 0) {
327 wfree(fullpath);
328 return NULL;
329 } else {
330 return fullpath;
332 } else {
333 return wstrdup(file);
337 flen = strlen(file);
338 for (i = 0; path_list[i] != NULL; i++) {
339 len = strlen(path_list[i]);
340 path = wmalloc(len + flen + 2);
341 path = memcpy(path, path_list[i], len);
342 path[len] = 0;
343 if (wstrlcat(path, "/", len + flen + 2) >= len + flen + 2 ||
344 wstrlcat(path, file, len + flen + 2) >= len + flen + 2) {
345 wfree(path);
346 return NULL;
348 /* expand tilde */
349 fullpath = wexpandpath(path);
350 wfree(path);
351 if (fullpath) {
352 /* check if file exists */
353 if (access(fullpath, F_OK) == 0) {
354 return fullpath;
356 wfree(fullpath);
360 return NULL;
363 char *wfindfileinarray(WMPropList *array, const char *file)
365 int i;
366 char *path;
367 int len, flen;
368 char *fullpath;
370 if (!file)
371 return NULL;
373 if (*file == '/' || *file == '~' || !array) {
374 if (access(file, F_OK) < 0) {
375 fullpath = wexpandpath(file);
376 if (!fullpath)
377 return NULL;
379 if (access(fullpath, F_OK) < 0) {
380 wfree(fullpath);
381 return NULL;
382 } else {
383 return fullpath;
385 } else {
386 return wstrdup(file);
390 flen = strlen(file);
391 for (i = 0; i < WMGetPropListItemCount(array); i++) {
392 WMPropList *prop;
393 char *p;
395 prop = WMGetFromPLArray(array, i);
396 if (!prop)
397 continue;
398 p = WMGetFromPLString(prop);
400 len = strlen(p);
401 path = wmalloc(len + flen + 2);
402 path = memcpy(path, p, len);
403 path[len] = 0;
404 if (wstrlcat(path, "/", len + flen + 2) >= len + flen + 2 ||
405 wstrlcat(path, file, len + flen + 2) >= len + flen + 2) {
406 wfree(path);
407 return NULL;
409 /* expand tilde */
410 fullpath = wexpandpath(path);
411 wfree(path);
412 if (fullpath) {
413 /* check if file exists */
414 if (access(fullpath, F_OK) == 0) {
415 return fullpath;
417 wfree(fullpath);
420 return NULL;
423 int wcopy_file(const char *dest_dir, const char *src_file, const char *dest_file)
425 char *path_dst;
426 int fd_src, fd_dst;
427 struct stat stat_src;
428 mode_t permission_dst;
429 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 */
430 char *buffer; /* The buffer is not created on the stack to avoid possible stack overflow as our buffer is big */
432 try_again_src:
433 fd_src = open(src_file, O_RDONLY | O_NOFOLLOW);
434 if (fd_src == -1) {
435 if (errno == EINTR)
436 goto try_again_src;
437 werror(_("Could not open input file \"%s\": %s"), src_file, strerror(errno));
438 return -1;
441 /* Only accept to copy regular files */
442 if (fstat(fd_src, &stat_src) != 0 || !S_ISREG(stat_src.st_mode)) {
443 close(fd_src);
444 return -1;
447 path_dst = wstrconcat(dest_dir, dest_file);
448 try_again_dst:
449 fd_dst = open(path_dst, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
450 if (fd_dst == -1) {
451 if (errno == EINTR)
452 goto try_again_dst;
453 werror(_("Could not create target file \"%s\": %s"), path_dst, strerror(errno));
454 wfree(path_dst);
455 close(fd_src);
456 return -1;
459 buffer = malloc(buffer_size); /* Don't use wmalloc to avoid the memset(0) we don't need */
460 if (buffer == NULL) {
461 werror(_("could not allocate memory for the copy buffer"));
462 close(fd_dst);
463 goto cleanup_and_return_failure;
466 for (;;) {
467 ssize_t size_data;
468 const char *write_ptr;
469 size_t write_remain;
471 try_again_read:
472 size_data = read(fd_src, buffer, buffer_size);
473 if (size_data == 0)
474 break; /* End of File have been reached */
475 if (size_data < 0) {
476 if (errno == EINTR)
477 goto try_again_read;
478 werror(_("could not read from file \"%s\": %s"), src_file, strerror(errno));
479 close(fd_dst);
480 goto cleanup_and_return_failure;
483 write_ptr = buffer;
484 write_remain = size_data;
485 while (write_remain > 0) {
486 ssize_t write_done;
488 try_again_write:
489 write_done = write(fd_dst, write_ptr, write_remain);
490 if (write_done < 0) {
491 if (errno == EINTR)
492 goto try_again_write;
493 werror(_("could not write data to file \"%s\": %s"), path_dst, strerror(errno));
494 close(fd_dst);
495 goto cleanup_and_return_failure;
497 write_ptr += write_done;
498 write_remain -= write_done;
502 /* Keep only the permission-related part of the field: */
503 permission_dst = stat_src.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX);
504 if (fchmod(fd_dst, permission_dst) != 0)
505 wwarning(_("could not set permission 0%03o on file \"%s\": %s"),
506 permission_dst, path_dst, strerror(errno));
508 if (close(fd_dst) != 0) {
509 werror(_("could not close the file \"%s\": %s"), path_dst, strerror(errno));
510 cleanup_and_return_failure:
511 free(buffer);
512 close(fd_src);
513 unlink(path_dst);
514 wfree(path_dst);
515 return -1;
518 free(buffer);
519 wfree(path_dst);
520 close(fd_src);
522 return 0;