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,
26 #include <sys/types.h>
44 static char *home
= NULL
;
51 #ifdef HAVE_SECURE_GETENV
52 tmp
= secure_getenv("HOME");
61 user
= getpwuid(getuid());
63 werror(_("could not get password entry for UID %i"), getuid());
71 home
= wstrdup(user
->pw_dir
);
77 * Return the home directory for the specified used
79 * If user not found, returns NULL, otherwise always returns a path that is
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
[] = "/";
91 user
= getpwnam(username
);
93 werror(_("could not get password entry for user %s"), username
);
103 char *wexpandpath(const char *path
)
105 const char *origpath
= path
;
106 char buffer2
[PATH_MAX
+ 2];
107 char buffer
[PATH_MAX
+ 2];
110 memset(buffer
, 0, PATH_MAX
+ 2);
116 if (*path
== '/' || *path
== 0) {
117 home
= wgethomedir();
118 if (strlen(home
) > PATH_MAX
||
119 wstrlcpy(buffer
, home
, sizeof(buffer
)) >= sizeof(buffer
))
124 while (*path
!= 0 && *path
!= '/') {
127 buffer2
[j
++] = *path
;
131 home
= getuserhomedir(buffer2
);
132 if (!home
|| wstrlcat(buffer
, home
, sizeof(buffer
)) >= sizeof(buffer
))
139 while (*path
!= 0 && i
<= PATH_MAX
) {
146 /* expand $(HOME) or $HOME style environment variables */
150 while (*path
!= 0 && *path
!= ')') {
153 buffer2
[j
++] = *(path
++);
158 tmp
= getenv(buffer2
);
163 if ((i
+= strlen(buffer2
) + 2) > PATH_MAX
)
166 if (wstrlcat(buffer
, "$(", sizeof(buffer
)) >= sizeof(buffer
) ||
167 wstrlcat(buffer
, buffer2
, sizeof(buffer
)) >= sizeof(buffer
))
169 if (*(path
-1)==')') {
170 if (++i
> PATH_MAX
||
171 wstrlcat(buffer
, ")", sizeof(buffer
)) >= sizeof(buffer
))
175 if ((i
+= strlen(tmp
)) > PATH_MAX
||
176 wstrlcat(buffer
, tmp
, sizeof(buffer
)) >= sizeof(buffer
))
181 while (*path
!= 0 && *path
!= '/') {
184 buffer2
[j
++] = *(path
++);
187 tmp
= getenv(buffer2
);
189 if ((i
+= strlen(buffer2
) + 1) > PATH_MAX
||
190 wstrlcat(buffer
, "$", sizeof(buffer
)) >= sizeof(buffer
) ||
191 wstrlcat(buffer
, buffer2
, sizeof(buffer
)) >= sizeof(buffer
))
194 if ((i
+= strlen(tmp
)) > PATH_MAX
||
195 wstrlcat(buffer
, tmp
, sizeof(buffer
)) >= sizeof(buffer
))
208 return wstrdup(buffer
);
211 errno
= ENAMETOOLONG
;
212 werror(_("could not expand %s"), origpath
);
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
)
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
)
236 *----------------------------------------------------------------------
238 * Finds a file in a : separated list of paths. ~ expansion is also
242 * The complete path for the file (in a newly allocated string) or
243 * NULL if the file was not found.
246 * A new string is allocated. It must be freed later.
248 *----------------------------------------------------------------------
250 char *wfindfile(const char *paths
, const char *file
)
253 const char *tmp
, *tmp2
;
260 if (*file
== '/' || *file
== '~' || *file
== '$' || !paths
|| *paths
== 0) {
261 if (access(file
, F_OK
) < 0) {
262 fullpath
= wexpandpath(file
);
266 if (access(fullpath
, F_OK
) < 0) {
273 return wstrdup(file
);
280 tmp
= skipchar(tmp
, ':');
283 tmp2
= nextchar(tmp
, ':');
285 path
= wmalloc(len
+ flen
+ 2);
286 path
= memcpy(path
, tmp
, len
);
288 if (path
[len
- 1] != '/' &&
289 wstrlcat(path
, "/", len
+ flen
+ 2) >= len
+ flen
+ 2) {
294 if (wstrlcat(path
, file
, len
+ flen
+ 2) >= len
+ flen
+ 2) {
299 fullpath
= wexpandpath(path
);
303 if (access(fullpath
, F_OK
) == 0) {
314 char *wfindfileinlist(char *const *path_list
, const char *file
)
324 if (*file
== '/' || *file
== '~' || !path_list
) {
325 if (access(file
, F_OK
) < 0) {
326 fullpath
= wexpandpath(file
);
330 if (access(fullpath
, F_OK
) < 0) {
337 return wstrdup(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
);
347 if (wstrlcat(path
, "/", len
+ flen
+ 2) >= len
+ flen
+ 2 ||
348 wstrlcat(path
, file
, len
+ flen
+ 2) >= len
+ flen
+ 2) {
353 fullpath
= wexpandpath(path
);
356 /* check if file exists */
357 if (access(fullpath
, F_OK
) == 0) {
367 char *wfindfileinarray(WMPropList
*array
, const char *file
)
377 if (*file
== '/' || *file
== '~' || !array
) {
378 if (access(file
, F_OK
) < 0) {
379 fullpath
= wexpandpath(file
);
383 if (access(fullpath
, F_OK
) < 0) {
390 return wstrdup(file
);
395 for (i
= 0; i
< WMGetPropListItemCount(array
); i
++) {
399 prop
= WMGetFromPLArray(array
, i
);
402 p
= WMGetFromPLString(prop
);
405 path
= wmalloc(len
+ flen
+ 2);
406 path
= memcpy(path
, p
, len
);
408 if (wstrlcat(path
, "/", len
+ flen
+ 2) >= len
+ flen
+ 2 ||
409 wstrlcat(path
, file
, len
+ flen
+ 2) >= len
+ flen
+ 2) {
414 fullpath
= wexpandpath(path
);
417 /* check if file exists */
418 if (access(fullpath
, F_OK
) == 0) {
427 int wcopy_file(const char *dest_dir
, const char *src_file
, const char *dest_file
)
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 */
437 fd_src
= open(src_file
, O_RDONLY
| O_NOFOLLOW
);
441 werror(_("Could not open input file \"%s\": %s"), src_file
, strerror(errno
));
445 /* Only accept to copy regular files */
446 if (fstat(fd_src
, &stat_src
) != 0 || !S_ISREG(stat_src
.st_mode
)) {
451 path_dst
= wstrconcat(dest_dir
, dest_file
);
453 fd_dst
= open(path_dst
, O_WRONLY
| O_CREAT
| O_TRUNC
, S_IRUSR
| S_IWUSR
);
457 werror(_("Could not create target file \"%s\": %s"), path_dst
, strerror(errno
));
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"));
467 goto cleanup_and_return_failure
;
472 const char *write_ptr
;
476 size_data
= read(fd_src
, buffer
, buffer_size
);
478 break; /* End of File have been reached */
482 werror(_("could not read from file \"%s\": %s"), src_file
, strerror(errno
));
484 goto cleanup_and_return_failure
;
488 write_remain
= size_data
;
489 while (write_remain
> 0) {
493 write_done
= write(fd_dst
, write_ptr
, write_remain
);
494 if (write_done
< 0) {
496 goto try_again_write
;
497 werror(_("could not write data to file \"%s\": %s"), path_dst
, strerror(errno
));
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
: