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>
42 const char *wgethomedir(void)
44 static char *home
= NULL
;
57 user
= getpwuid(getuid());
59 werror(_("could not get password entry for UID %i"), getuid());
67 home
= wstrdup(user
->pw_dir
);
73 * Return the home directory for the specified used
75 * If user not found, returns NULL, otherwise always returns a path that is
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
[] = "/";
87 user
= getpwnam(username
);
89 werror(_("could not get password entry for user %s"), username
);
99 char *wexpandpath(const char *path
)
101 const char *origpath
= path
;
102 char buffer2
[PATH_MAX
+ 2];
103 char buffer
[PATH_MAX
+ 2];
106 memset(buffer
, 0, PATH_MAX
+ 2);
112 if (*path
== '/' || *path
== 0) {
113 home
= wgethomedir();
114 if (strlen(home
) > PATH_MAX
||
115 wstrlcpy(buffer
, home
, sizeof(buffer
)) >= sizeof(buffer
))
120 while (*path
!= 0 && *path
!= '/') {
123 buffer2
[j
++] = *path
;
127 home
= getuserhomedir(buffer2
);
128 if (!home
|| wstrlcat(buffer
, home
, sizeof(buffer
)) >= sizeof(buffer
))
135 while (*path
!= 0 && i
<= PATH_MAX
) {
142 /* expand $(HOME) or $HOME style environment variables */
146 while (*path
!= 0 && *path
!= ')') {
149 buffer2
[j
++] = *(path
++);
154 tmp
= getenv(buffer2
);
159 if ((i
+= strlen(buffer2
) + 2) > PATH_MAX
)
162 if (wstrlcat(buffer
, "$(", sizeof(buffer
)) >= sizeof(buffer
) ||
163 wstrlcat(buffer
, buffer2
, sizeof(buffer
)) >= sizeof(buffer
))
165 if (*(path
-1)==')') {
166 if (++i
> PATH_MAX
||
167 wstrlcat(buffer
, ")", sizeof(buffer
)) >= sizeof(buffer
))
171 if ((i
+= strlen(tmp
)) > PATH_MAX
||
172 wstrlcat(buffer
, tmp
, sizeof(buffer
)) >= sizeof(buffer
))
177 while (*path
!= 0 && *path
!= '/') {
180 buffer2
[j
++] = *(path
++);
183 tmp
= getenv(buffer2
);
185 if ((i
+= strlen(buffer2
) + 1) > PATH_MAX
||
186 wstrlcat(buffer
, "$", sizeof(buffer
)) >= sizeof(buffer
) ||
187 wstrlcat(buffer
, buffer2
, sizeof(buffer
)) >= sizeof(buffer
))
190 if ((i
+= strlen(tmp
)) > PATH_MAX
||
191 wstrlcat(buffer
, tmp
, sizeof(buffer
)) >= sizeof(buffer
))
204 return wstrdup(buffer
);
207 errno
= ENAMETOOLONG
;
208 werror(_("could not expand %s"), origpath
);
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
)
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
)
232 *----------------------------------------------------------------------
234 * Finds a file in a : separated list of paths. ~ expansion is also
238 * The complete path for the file (in a newly allocated string) or
239 * NULL if the file was not found.
242 * A new string is allocated. It must be freed later.
244 *----------------------------------------------------------------------
246 char *wfindfile(const char *paths
, const char *file
)
249 const char *tmp
, *tmp2
;
256 if (*file
== '/' || *file
== '~' || *file
== '$' || !paths
|| *paths
== 0) {
257 if (access(file
, F_OK
) < 0) {
258 fullpath
= wexpandpath(file
);
262 if (access(fullpath
, F_OK
) < 0) {
269 return wstrdup(file
);
276 tmp
= skipchar(tmp
, ':');
279 tmp2
= nextchar(tmp
, ':');
281 path
= wmalloc(len
+ flen
+ 2);
282 path
= memcpy(path
, tmp
, len
);
284 if (path
[len
- 1] != '/' &&
285 wstrlcat(path
, "/", len
+ flen
+ 2) >= len
+ flen
+ 2) {
290 if (wstrlcat(path
, file
, len
+ flen
+ 2) >= len
+ flen
+ 2) {
295 fullpath
= wexpandpath(path
);
299 if (access(fullpath
, F_OK
) == 0) {
310 char *wfindfileinlist(char *const *path_list
, const char *file
)
320 if (*file
== '/' || *file
== '~' || !path_list
) {
321 if (access(file
, F_OK
) < 0) {
322 fullpath
= wexpandpath(file
);
326 if (access(fullpath
, F_OK
) < 0) {
333 return wstrdup(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
);
343 if (wstrlcat(path
, "/", len
+ flen
+ 2) >= len
+ flen
+ 2 ||
344 wstrlcat(path
, file
, len
+ flen
+ 2) >= len
+ flen
+ 2) {
349 fullpath
= wexpandpath(path
);
352 /* check if file exists */
353 if (access(fullpath
, F_OK
) == 0) {
363 char *wfindfileinarray(WMPropList
*array
, const char *file
)
373 if (*file
== '/' || *file
== '~' || !array
) {
374 if (access(file
, F_OK
) < 0) {
375 fullpath
= wexpandpath(file
);
379 if (access(fullpath
, F_OK
) < 0) {
386 return wstrdup(file
);
391 for (i
= 0; i
< WMGetPropListItemCount(array
); i
++) {
395 prop
= WMGetFromPLArray(array
, i
);
398 p
= WMGetFromPLString(prop
);
401 path
= wmalloc(len
+ flen
+ 2);
402 path
= memcpy(path
, p
, len
);
404 if (wstrlcat(path
, "/", len
+ flen
+ 2) >= len
+ flen
+ 2 ||
405 wstrlcat(path
, file
, len
+ flen
+ 2) >= len
+ flen
+ 2) {
410 fullpath
= wexpandpath(path
);
413 /* check if file exists */
414 if (access(fullpath
, F_OK
) == 0) {
423 int wcopy_file(const char *dest_dir
, const char *src_file
, const char *dest_file
)
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 */
433 fd_src
= open(src_file
, O_RDONLY
| O_NOFOLLOW
);
437 werror(_("Could not open input file \"%s\": %s"), src_file
, strerror(errno
));
441 /* Only accept to copy regular files */
442 if (fstat(fd_src
, &stat_src
) != 0 || !S_ISREG(stat_src
.st_mode
)) {
447 path_dst
= wstrconcat(dest_dir
, dest_file
);
449 fd_dst
= open(path_dst
, O_WRONLY
| O_CREAT
| O_TRUNC
, S_IRUSR
| S_IWUSR
);
453 werror(_("Could not create target file \"%s\": %s"), path_dst
, strerror(errno
));
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"));
463 goto cleanup_and_return_failure
;
468 const char *write_ptr
;
472 size_data
= read(fd_src
, buffer
, buffer_size
);
474 break; /* End of File have been reached */
478 werror(_("could not read from file \"%s\": %s"), src_file
, strerror(errno
));
480 goto cleanup_and_return_failure
;
484 write_remain
= size_data
;
485 while (write_remain
> 0) {
489 write_done
= write(fd_dst
, write_ptr
, write_remain
);
490 if (write_done
< 0) {
492 goto try_again_write
;
493 werror(_("could not write data to file \"%s\": %s"), path_dst
, strerror(errno
));
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
: