12 #include <sys/types.h>
13 #include <sys/stat.h> /* OS/2 needs this after sys/types.h */
15 #include <dirent.h> /* OS/2 needs this after sys/types.h */
18 #include <fcntl.h> /* OS/2 needs this after sys/types.h */
27 #ifdef TIME_WITH_SYS_TIME
28 #ifdef HAVE_SYS_TIME_H
35 #if defined(TM_IN_SYS_TIME) && defined(HAVE_SYS_TIME_H)
37 #elif defined(HAVE_TIME_H)
44 #include "osdep/osdep.h"
45 #include "util/conv.h"
46 #include "util/error.h"
47 #include "util/file.h"
48 #include "util/memory.h"
49 #include "util/string.h"
52 /* Not that these two would be so useful for portability (they are ANSI C) but
53 * they encapsulate the lowlevel stuff (need for <unistd.h>) nicely. */
56 file_exists(const unsigned char *filename
)
59 return access(filename
, F_OK
) >= 0;
63 return stat(filename
, &buf
) >= 0;
68 file_can_read(const unsigned char *filename
)
71 return access(filename
, R_OK
) >= 0;
73 FILE *f
= fopen(filename
, "rb");
82 file_is_dir(const unsigned char *filename
)
86 if (stat(filename
, &st
))
89 return S_ISDIR(st
.st_mode
);
93 get_filename_position(unsigned char *filename
)
98 if_assert_failed
return NULL
;
100 for (pos
= filename
; *pos
; pos
++)
101 if (dir_sep(*pos
)) filename
= pos
+ 1;
107 expand_tilde(unsigned char *filename
)
111 if (!init_string(&file
)) return NULL
;
113 if (filename
[0] == '~') {
114 if (!filename
[1] || dir_sep(filename
[1])) {
115 unsigned char *home
= getenv("HOME");
118 add_to_string(&file
, home
);
123 struct passwd
*passwd
= NULL
;
124 unsigned char *user
= filename
+ 1;
127 while (user
[userlen
] && !dir_sep(user
[userlen
]))
130 user
= memacpy(user
, userlen
);
132 passwd
= getpwnam(user
);
136 if (passwd
&& passwd
->pw_dir
) {
137 add_to_string(&file
, passwd
->pw_dir
);
138 filename
+= 1 + userlen
;
144 add_to_string(&file
, filename
);
150 get_unique_name(unsigned char *fileprefix
)
152 unsigned char *file
= fileprefix
;
153 int fileprefixlen
= strlen(fileprefix
);
158 while (file_exists(file
)) {
159 if (!(suffix
< memtrigger
)) {
161 INTERNAL("Too big suffix in get_unique_name().");
165 if (file
!= fileprefix
) mem_free(file
);
166 file
= mem_alloc(fileprefixlen
+ 2 + digits
);
167 if (!file
) return NULL
;
169 memcpy(file
, fileprefix
, fileprefixlen
);
170 file
[fileprefixlen
] = '.';
173 longcat(&file
[fileprefixlen
+ 1], NULL
, suffix
, digits
+ 1, 0);
181 get_tempdir_filename(unsigned char *name
)
183 unsigned char *tmpdir
= getenv("TMPDIR");
185 if (!tmpdir
|| !*tmpdir
) tmpdir
= getenv("TMP");
186 if (!tmpdir
|| !*tmpdir
) tmpdir
= getenv("TEMPDIR");
187 if (!tmpdir
|| !*tmpdir
) tmpdir
= getenv("TEMP");
188 if (!tmpdir
|| !*tmpdir
) tmpdir
= "/tmp";
190 return straconcat(tmpdir
, "/", name
, NULL
);
194 file_read_line(unsigned char *line
, size_t *size
, FILE *file
, int *lineno
)
199 line
= mem_alloc(MAX_STR_LEN
);
206 while (fgets(line
+ offset
, *size
- offset
, file
)) {
207 unsigned char *linepos
= strchr(line
+ offset
, '\n');
210 /* Test if the line buffer should be increase because
211 * it was continued and could not fit. */
212 unsigned char *newline
;
213 int next
= getc(file
);
216 /* We are on the last line. */
221 /* Undo our dammage */
224 *size
+= MAX_STR_LEN
;
226 newline
= mem_realloc(line
, *size
);
233 /* A whole line was read. Fetch next into the buffer if
234 * the line is 'continued'. */
237 while (line
< linepos
&& isspace(*linepos
))
240 if (*linepos
!= '\\') {
241 *(linepos
+ 1) = '\0';
244 offset
= linepos
- line
- 1;
252 /* Some mkstemp() implementations do not set safe permissions,
253 * so it may result in temporary files readable by all users.
254 * This may be a problem when textarea fields are edited through
255 * an external editor (see src/viewer/text/textarea.c).
257 * From 2001-12-23 mkstemp(3) gnu man page:
260 * The file is then created with mode read/write and permissions 0666
261 * (glibc 2.0.6 and earlier), 0600 (glibc 2.0.7 and later).
265 * The old behaviour (creating a file with mode 0666) may be a security
266 * risk, especially since other Unix flavours use 0600, and somebody
267 * might overlook this detail when porting programs.
268 * More generally, the POSIX specification does not say anything
269 * about file modes, so the application should make sure its umask is
270 * set appropriately before calling mkstemp.
273 safe_mkstemp(unsigned char *template)
275 #ifndef CONFIG_OS_WIN32
276 mode_t saved_mask
= umask(S_IXUSR
| S_IRWXG
| S_IRWXO
);
278 int fd
= mkstemp(template);
279 #ifndef CONFIG_OS_WIN32
286 /* The stat_* functions set the various attributes for directory entries. */
289 stat_type(struct string
*string
, struct stat
*stp
)
291 unsigned char c
= '?';
294 if (S_ISDIR(stp
->st_mode
)) c
= 'd';
295 else if (S_ISREG(stp
->st_mode
)) c
= '-';
297 else if (S_ISBLK(stp
->st_mode
)) c
= 'b';
300 else if (S_ISCHR(stp
->st_mode
)) c
= 'c';
303 else if (S_ISFIFO(stp
->st_mode
)) c
= 'p';
306 else if (S_ISLNK(stp
->st_mode
)) c
= 'l';
309 else if (S_ISSOCK(stp
->st_mode
)) c
= 's';
312 else if (S_ISNWK(stp
->st_mode
)) c
= 'n';
316 add_char_to_string(string
, c
);
320 stat_mode(struct string
*string
, struct stat
*stp
)
322 #ifdef FS_UNIX_RIGHTS
323 unsigned char rwx
[10] = "---------";
326 mode_t mode
= stp
->st_mode
;
329 /* Set permissions attributes for user, group and other */
330 for (shift
= 0; shift
<= 6; shift
+= 3) {
331 mode_t m
= mode
<< shift
;
333 if (m
& S_IRUSR
) rwx
[shift
+ 0] = 'r';
334 if (m
& S_IWUSR
) rwx
[shift
+ 1] = 'w';
335 if (m
& S_IXUSR
) rwx
[shift
+ 2] = 'x';
340 rwx
[2] = (mode
& S_IXUSR
) ? 's' : 'S';
344 rwx
[5] = (mode
& S_IXGRP
) ? 's' : 'S';
348 rwx
[8] = (mode
& S_IXOTH
) ? 't' : 'T';
351 add_to_string(string
, rwx
);
353 add_char_to_string(string
, ' ');
357 stat_links(struct string
*string
, struct stat
*stp
)
359 #ifdef FS_UNIX_HARDLINKS
361 add_to_string(string
, " ");
363 unsigned char lnk
[64];
365 ulongcat(lnk
, NULL
, stp
->st_nlink
, 3, ' ');
366 add_to_string(string
, lnk
);
367 add_char_to_string(string
, ' ');
373 stat_user(struct string
*string
, struct stat
*stp
)
376 static unsigned char last_user
[64];
377 static int last_uid
= -1;
380 add_to_string(string
, " ");
384 if (stp
->st_uid
!= last_uid
) {
385 struct passwd
*pwd
= getpwuid(stp
->st_uid
);
387 if (!pwd
|| !pwd
->pw_name
)
388 /* ulongcat() can't pad from right. */
389 sprintf(last_user
, "%-8d", (int) stp
->st_uid
);
391 sprintf(last_user
, "%-8.8s", pwd
->pw_name
);
393 last_uid
= stp
->st_uid
;
396 add_to_string(string
, last_user
);
397 add_char_to_string(string
, ' ');
402 stat_group(struct string
*string
, struct stat
*stp
)
405 static unsigned char last_group
[64];
406 static int last_gid
= -1;
409 add_to_string(string
, " ");
413 if (stp
->st_gid
!= last_gid
) {
414 struct group
*grp
= getgrgid(stp
->st_gid
);
416 if (!grp
|| !grp
->gr_name
)
417 /* ulongcat() can't pad from right. */
418 sprintf(last_group
, "%-8d", (int) stp
->st_gid
);
420 sprintf(last_group
, "%-8.8s", grp
->gr_name
);
422 last_gid
= stp
->st_gid
;
425 add_to_string(string
, last_group
);
426 add_char_to_string(string
, ' ');
431 stat_size(struct string
*string
, struct stat
*stp
)
433 /* Check if st_size will cause overflow. */
434 /* FIXME: See bug 497 for info about support for big files. */
435 if (!stp
|| stp
->st_size
!= (unsigned long) stp
->st_size
) {
436 add_to_string(string
, " ");
439 unsigned char size
[64];
442 /* First try to fit the file size into 8 digits ... */
443 width
= ulongcat(size
, NULL
, stp
->st_size
, width
, ' ');
444 if (0 < width
&& width
< sizeof(size
)) {
445 /* ... if that is not enough expand the size buffer.
446 * This will only break the column alignment of the size
447 * attribute if really needed. */
448 ulongcat(size
, NULL
, stp
->st_size
, width
, ' ');
451 add_to_string(string
, size
);
452 add_char_to_string(string
, ' ');
457 stat_date(struct string
*string
, struct stat
*stp
)
461 time_t current_time
= time(NULL
);
462 time_t when
= stp
->st_mtime
;
465 if (current_time
> when
+ 6L * 30L * 24L * 60L * 60L
466 || current_time
< when
- 60L * 60L)
471 add_date_to_string(string
, fmt
, &when
);
472 add_char_to_string(string
, ' ');
476 add_to_string(string
, " ");
481 compare_dir_entries(struct directory_entry
*d1
, struct directory_entry
*d2
)
483 if (d1
->name
[0] == '.' && d1
->name
[1] == '.' && !d1
->name
[2]) return -1;
484 if (d2
->name
[0] == '.' && d2
->name
[1] == '.' && !d2
->name
[2]) return 1;
485 if (d1
->attrib
[0] == 'd' && d2
->attrib
[0] != 'd') return -1;
486 if (d1
->attrib
[0] != 'd' && d2
->attrib
[0] == 'd') return 1;
487 return strcmp(d1
->name
, d2
->name
);
491 /* This function decides whether a file should be shown in directory listing or
492 * not. Returns according boolean value. */
494 file_visible(unsigned char *name
, int get_hidden_files
, int is_root_directory
)
496 /* Always show everything not beginning with a dot. */
500 /* Always hide the "." directory. */
504 /* Always show the ".." directory (but for root directory). */
505 if (name
[1] == '.' && name
[2] == '\0')
506 return !is_root_directory
;
508 /* Others like ".x" or "..foo" are shown if get_hidden_files
510 return get_hidden_files
;
513 /* First information such as permissions is gathered for each directory entry.
514 * All entries are then sorted and finally the sorted entries are added to the
515 * @data->fragment one by one. */
516 struct directory_entry
*
517 get_directory_entries(unsigned char *dirname
, int get_hidden
)
519 struct directory_entry
*entries
= NULL
;
522 struct dirent
*entry
;
523 int is_root_directory
= dirname
[0] == '/' && !dirname
[1];
525 directory
= opendir(dirname
);
526 if (!directory
) return NULL
;
528 while ((entry
= readdir(directory
))) {
529 struct stat st
, *stp
;
530 struct directory_entry
*new_entries
;
532 struct string attrib
;
534 if (!file_visible(entry
->d_name
, get_hidden
, is_root_directory
))
537 new_entries
= mem_realloc(entries
, (size
+ 2) * sizeof(*new_entries
));
538 if (!new_entries
) continue;
539 entries
= new_entries
;
541 /* We allocate the full path because it is used in a few places
542 * which means less allocation although a bit more short term
544 name
= straconcat(dirname
, entry
->d_name
, NULL
);
547 if (!init_string(&attrib
)) {
552 #ifdef FS_UNIX_SOFTLINKS
553 stp
= (lstat(name
, &st
)) ? NULL
: &st
;
555 stp
= (stat(name
, &st
)) ? NULL
: &st
;
558 stat_type(&attrib
, stp
);
559 stat_mode(&attrib
, stp
);
560 stat_links(&attrib
, stp
);
561 stat_user(&attrib
, stp
);
562 stat_group(&attrib
, stp
);
563 stat_size(&attrib
, stp
);
564 stat_date(&attrib
, stp
);
566 entries
[size
].name
= name
;
567 entries
[size
].attrib
= attrib
.source
;
574 /* We may have allocated space for entries but added none. */
575 mem_free_if(entries
);
579 qsort(entries
, size
, sizeof(*entries
),
580 (int (*)(const void *, const void *)) compare_dir_entries
);
582 memset(&entries
[size
], 0, sizeof(*entries
));
587 /* Recursively create directories in a path. The last element in the path is
588 * taken to be a filename, and simply ignored */
590 mkalldirs(const unsigned char *path
)
592 int pos
, len
, ret
= 0;
595 if (!*path
) return -1;
597 /* Make a copy of path, to be able to write to it. Otherwise, the
598 * function is unsafe if called with a read-only char *argument. */
599 len
= strlen(path
) + 1;
602 memcpy(p
, path
, len
);
604 for (pos
= 1; p
[pos
]; pos
++) {
605 unsigned char separator
= p
[pos
];
607 if (!dir_sep(separator
))
612 ret
= mkdir(p
, S_IRWXU
);
616 if (ret
< 0 && errno
!= EEXIST
)