2005-09-15 09:58:31 -04:00
|
|
|
/* File utilities */
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <ctype.h>
|
2006-01-05 04:49:05 -05:00
|
|
|
#include <errno.h>
|
2005-09-15 09:58:31 -04:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h> /* OS/2 needs this after sys/types.h */
|
|
|
|
#ifdef HAVE_DIRENT_H
|
|
|
|
#include <dirent.h> /* OS/2 needs this after sys/types.h */
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_FCNTL_H
|
|
|
|
#include <fcntl.h> /* OS/2 needs this after sys/types.h */
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_PWD_H
|
|
|
|
#include <pwd.h>
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef TIME_WITH_SYS_TIME
|
|
|
|
#ifdef HAVE_SYS_TIME_H
|
|
|
|
#include <sys/time.h>
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_TIME_H
|
|
|
|
#include <time.h>
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
#if defined(TM_IN_SYS_TIME) && defined(HAVE_SYS_TIME_H)
|
|
|
|
#include <sys/time.h>
|
|
|
|
#elif defined(HAVE_TIME_H)
|
|
|
|
#include <time.h>
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "elinks.h"
|
|
|
|
|
|
|
|
#include "osdep/osdep.h"
|
|
|
|
#include "util/conv.h"
|
|
|
|
#include "util/error.h"
|
|
|
|
#include "util/file.h"
|
|
|
|
#include "util/memory.h"
|
|
|
|
#include "util/string.h"
|
2006-01-04 15:41:56 -05:00
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
/* Not that these two would be so useful for portability (they are ANSI C) but
|
|
|
|
* they encapsulate the lowlevel stuff (need for <unistd.h>) nicely. */
|
|
|
|
|
|
|
|
int
|
|
|
|
file_exists(const unsigned char *filename)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_ACCESS
|
2006-01-04 15:41:56 -05:00
|
|
|
return access(filename, F_OK) >= 0;
|
2005-09-15 09:58:31 -04:00
|
|
|
#else
|
|
|
|
struct stat buf;
|
|
|
|
|
2006-01-04 15:41:56 -05:00
|
|
|
return stat(filename, &buf) >= 0;
|
2005-09-15 09:58:31 -04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
file_can_read(const unsigned char *filename)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_ACCESS
|
|
|
|
return access(filename, R_OK) >= 0;
|
|
|
|
#else
|
|
|
|
FILE *f = fopen(filename, "rb");
|
|
|
|
int ok = !!f;
|
|
|
|
|
|
|
|
if (f) fclose(f);
|
|
|
|
return ok;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
file_is_dir(const unsigned char *filename)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
if (stat(filename, &st))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return S_ISDIR(st.st_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char *
|
|
|
|
get_filename_position(unsigned char *filename)
|
|
|
|
{
|
|
|
|
unsigned char *pos;
|
|
|
|
|
|
|
|
assert(filename);
|
|
|
|
if_assert_failed return NULL;
|
|
|
|
|
|
|
|
for (pos = filename; *pos; pos++)
|
|
|
|
if (dir_sep(*pos)) filename = pos + 1;
|
|
|
|
|
|
|
|
return filename;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char *
|
|
|
|
expand_tilde(unsigned char *filename)
|
|
|
|
{
|
|
|
|
struct string file;
|
|
|
|
|
|
|
|
if (!init_string(&file)) return NULL;
|
|
|
|
|
|
|
|
if (filename[0] == '~') {
|
|
|
|
if (!filename[1] || dir_sep(filename[1])) {
|
|
|
|
unsigned char *home = getenv("HOME");
|
|
|
|
|
|
|
|
if (home) {
|
|
|
|
add_to_string(&file, home);
|
|
|
|
filename++;
|
|
|
|
}
|
|
|
|
#ifdef HAVE_GETPWNAM
|
|
|
|
} else {
|
|
|
|
struct passwd *passwd = NULL;
|
|
|
|
unsigned char *user = filename + 1;
|
|
|
|
int userlen = 0;
|
|
|
|
|
|
|
|
while (user[userlen] && !dir_sep(user[userlen]))
|
|
|
|
userlen++;
|
|
|
|
|
|
|
|
user = memacpy(user, userlen);
|
|
|
|
if (user) {
|
|
|
|
passwd = getpwnam(user);
|
|
|
|
mem_free(user);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (passwd && passwd->pw_dir) {
|
|
|
|
add_to_string(&file, passwd->pw_dir);
|
|
|
|
filename += 1 + userlen;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
add_to_string(&file, filename);
|
|
|
|
|
|
|
|
return file.source;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char *
|
|
|
|
get_unique_name(unsigned char *fileprefix)
|
|
|
|
{
|
|
|
|
unsigned char *file = fileprefix;
|
|
|
|
int fileprefixlen = strlen(fileprefix);
|
|
|
|
int memtrigger = 1;
|
|
|
|
int suffix = 1;
|
|
|
|
int digits = 0;
|
|
|
|
|
|
|
|
while (file_exists(file)) {
|
|
|
|
if (!(suffix < memtrigger)) {
|
|
|
|
if (suffix >= 10000)
|
|
|
|
INTERNAL("Too big suffix in get_unique_name().");
|
|
|
|
memtrigger *= 10;
|
|
|
|
digits++;
|
|
|
|
|
|
|
|
if (file != fileprefix) mem_free(file);
|
|
|
|
file = mem_alloc(fileprefixlen + 2 + digits);
|
|
|
|
if (!file) return NULL;
|
|
|
|
|
|
|
|
memcpy(file, fileprefix, fileprefixlen);
|
|
|
|
file[fileprefixlen] = '.';
|
|
|
|
}
|
|
|
|
|
|
|
|
longcat(&file[fileprefixlen + 1], NULL, suffix, digits + 1, 0);
|
|
|
|
suffix++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char *
|
|
|
|
get_tempdir_filename(unsigned char *name)
|
|
|
|
{
|
|
|
|
unsigned char *tmpdir = getenv("TMPDIR");
|
|
|
|
|
|
|
|
if (!tmpdir || !*tmpdir) tmpdir = getenv("TMP");
|
|
|
|
if (!tmpdir || !*tmpdir) tmpdir = getenv("TEMPDIR");
|
|
|
|
if (!tmpdir || !*tmpdir) tmpdir = getenv("TEMP");
|
|
|
|
if (!tmpdir || !*tmpdir) tmpdir = "/tmp";
|
|
|
|
|
|
|
|
return straconcat(tmpdir, "/", name, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char *
|
|
|
|
file_read_line(unsigned char *line, size_t *size, FILE *file, int *lineno)
|
|
|
|
{
|
|
|
|
size_t offset = 0;
|
|
|
|
|
|
|
|
if (!line) {
|
|
|
|
line = mem_alloc(MAX_STR_LEN);
|
|
|
|
if (!line)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
*size = MAX_STR_LEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (fgets(line + offset, *size - offset, file)) {
|
|
|
|
unsigned char *linepos = strchr(line + offset, '\n');
|
|
|
|
|
|
|
|
if (!linepos) {
|
|
|
|
/* Test if the line buffer should be increase because
|
|
|
|
* it was continued and could not fit. */
|
|
|
|
unsigned char *newline;
|
|
|
|
int next = getc(file);
|
|
|
|
|
|
|
|
if (next == EOF) {
|
|
|
|
/* We are on the last line. */
|
|
|
|
(*lineno)++;
|
|
|
|
return line;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Undo our dammage */
|
|
|
|
ungetc(next, file);
|
|
|
|
offset = *size - 1;
|
|
|
|
*size += MAX_STR_LEN;
|
|
|
|
|
|
|
|
newline = mem_realloc(line, *size);
|
|
|
|
if (!newline)
|
|
|
|
break;
|
|
|
|
line = newline;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A whole line was read. Fetch next into the buffer if
|
|
|
|
* the line is 'continued'. */
|
|
|
|
(*lineno)++;
|
|
|
|
|
|
|
|
while (line < linepos && isspace(*linepos))
|
|
|
|
linepos--;
|
|
|
|
|
|
|
|
if (*linepos != '\\') {
|
|
|
|
*(linepos + 1) = '\0';
|
|
|
|
return line;
|
|
|
|
}
|
|
|
|
offset = linepos - line - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
mem_free_if(line);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Some mkstemp() implementations do not set safe permissions,
|
|
|
|
* so it may result in temporary files readable by all users.
|
|
|
|
* This may be a problem when textarea fields are edited through
|
|
|
|
* an external editor (see src/viewer/text/textarea.c).
|
|
|
|
*
|
|
|
|
* From 2001-12-23 mkstemp(3) gnu man page:
|
|
|
|
*
|
|
|
|
* ...
|
|
|
|
* The file is then created with mode read/write and permissions 0666
|
|
|
|
* (glibc 2.0.6 and earlier), 0600 (glibc 2.0.7 and later).
|
|
|
|
* ...
|
|
|
|
*
|
|
|
|
* NOTES:
|
|
|
|
* The old behaviour (creating a file with mode 0666) may be a security
|
|
|
|
* risk, especially since other Unix flavours use 0600, and somebody
|
|
|
|
* might overlook this detail when porting programs.
|
|
|
|
* More generally, the POSIX specification does not say anything
|
|
|
|
* about file modes, so the application should make sure its umask is
|
|
|
|
* set appropriately before calling mkstemp.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
safe_mkstemp(unsigned char *template)
|
|
|
|
{
|
2006-01-10 17:35:22 -05:00
|
|
|
mode_t saved_mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
|
2005-09-15 09:58:31 -04:00
|
|
|
int fd = mkstemp(template);
|
|
|
|
|
|
|
|
umask(saved_mask);
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* The stat_* functions set the various attributes for directory entries. */
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
stat_type(struct string *string, struct stat *stp)
|
|
|
|
{
|
|
|
|
unsigned char c = '?';
|
|
|
|
|
|
|
|
if (stp) {
|
|
|
|
if (S_ISDIR(stp->st_mode)) c = 'd';
|
|
|
|
else if (S_ISREG(stp->st_mode)) c = '-';
|
|
|
|
#ifdef S_ISBLK
|
|
|
|
else if (S_ISBLK(stp->st_mode)) c = 'b';
|
|
|
|
#endif
|
|
|
|
#ifdef S_ISCHR
|
|
|
|
else if (S_ISCHR(stp->st_mode)) c = 'c';
|
|
|
|
#endif
|
|
|
|
#ifdef S_ISFIFO
|
|
|
|
else if (S_ISFIFO(stp->st_mode)) c = 'p';
|
|
|
|
#endif
|
|
|
|
#ifdef S_ISLNK
|
|
|
|
else if (S_ISLNK(stp->st_mode)) c = 'l';
|
|
|
|
#endif
|
|
|
|
#ifdef S_ISSOCK
|
|
|
|
else if (S_ISSOCK(stp->st_mode)) c = 's';
|
|
|
|
#endif
|
|
|
|
#ifdef S_ISNWK
|
|
|
|
else if (S_ISNWK(stp->st_mode)) c = 'n';
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
add_char_to_string(string, c);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
stat_mode(struct string *string, struct stat *stp)
|
|
|
|
{
|
|
|
|
#ifdef FS_UNIX_RIGHTS
|
|
|
|
unsigned char rwx[10] = "---------";
|
|
|
|
|
|
|
|
if (stp) {
|
2006-01-10 17:35:22 -05:00
|
|
|
mode_t mode = stp->st_mode;
|
|
|
|
unsigned int shift;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
/* Set permissions attributes for user, group and other */
|
|
|
|
for (shift = 0; shift <= 6; shift += 3) {
|
2006-01-10 17:35:22 -05:00
|
|
|
mode_t m = mode << shift;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (m & S_IRUSR) rwx[shift + 0] = 'r';
|
|
|
|
if (m & S_IWUSR) rwx[shift + 1] = 'w';
|
|
|
|
if (m & S_IXUSR) rwx[shift + 2] = 'x';
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef S_ISUID
|
|
|
|
if (mode & S_ISUID)
|
|
|
|
rwx[2] = (mode & S_IXUSR) ? 's' : 'S';
|
|
|
|
#endif
|
|
|
|
#ifdef S_ISGID
|
|
|
|
if (mode & S_ISGID)
|
|
|
|
rwx[5] = (mode & S_IXGRP) ? 's' : 'S';
|
|
|
|
#endif
|
|
|
|
#ifdef S_ISVTX
|
|
|
|
if (mode & S_ISVTX)
|
|
|
|
rwx[8] = (mode & S_IXOTH) ? 't' : 'T';
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
add_to_string(string, rwx);
|
|
|
|
#endif
|
|
|
|
add_char_to_string(string, ' ');
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
stat_links(struct string *string, struct stat *stp)
|
|
|
|
{
|
|
|
|
#ifdef FS_UNIX_HARDLINKS
|
|
|
|
if (!stp) {
|
|
|
|
add_to_string(string, " ");
|
|
|
|
} else {
|
|
|
|
unsigned char lnk[64];
|
|
|
|
|
|
|
|
ulongcat(lnk, NULL, stp->st_nlink, 3, ' ');
|
|
|
|
add_to_string(string, lnk);
|
|
|
|
add_char_to_string(string, ' ');
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
stat_user(struct string *string, struct stat *stp)
|
|
|
|
{
|
|
|
|
#ifdef FS_UNIX_USERS
|
|
|
|
static unsigned char last_user[64];
|
|
|
|
static int last_uid = -1;
|
|
|
|
|
|
|
|
if (!stp) {
|
|
|
|
add_to_string(string, " ");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stp->st_uid != last_uid) {
|
|
|
|
struct passwd *pwd = getpwuid(stp->st_uid);
|
|
|
|
|
|
|
|
if (!pwd || !pwd->pw_name)
|
|
|
|
/* ulongcat() can't pad from right. */
|
|
|
|
sprintf(last_user, "%-8d", (int) stp->st_uid);
|
|
|
|
else
|
|
|
|
sprintf(last_user, "%-8.8s", pwd->pw_name);
|
|
|
|
|
|
|
|
last_uid = stp->st_uid;
|
|
|
|
}
|
|
|
|
|
|
|
|
add_to_string(string, last_user);
|
|
|
|
add_char_to_string(string, ' ');
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
stat_group(struct string *string, struct stat *stp)
|
|
|
|
{
|
|
|
|
#ifdef FS_UNIX_USERS
|
|
|
|
static unsigned char last_group[64];
|
|
|
|
static int last_gid = -1;
|
|
|
|
|
|
|
|
if (!stp) {
|
|
|
|
add_to_string(string, " ");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stp->st_gid != last_gid) {
|
|
|
|
struct group *grp = getgrgid(stp->st_gid);
|
|
|
|
|
|
|
|
if (!grp || !grp->gr_name)
|
|
|
|
/* ulongcat() can't pad from right. */
|
|
|
|
sprintf(last_group, "%-8d", (int) stp->st_gid);
|
|
|
|
else
|
|
|
|
sprintf(last_group, "%-8.8s", grp->gr_name);
|
|
|
|
|
|
|
|
last_gid = stp->st_gid;
|
|
|
|
}
|
|
|
|
|
|
|
|
add_to_string(string, last_group);
|
|
|
|
add_char_to_string(string, ' ');
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
stat_size(struct string *string, struct stat *stp)
|
|
|
|
{
|
|
|
|
/* Check if st_size will cause overflow. */
|
|
|
|
/* FIXME: See bug 497 for info about support for big files. */
|
|
|
|
if (!stp || stp->st_size != (unsigned long) stp->st_size) {
|
|
|
|
add_to_string(string, " ");
|
|
|
|
|
|
|
|
} else {
|
|
|
|
unsigned char size[64];
|
|
|
|
int width = 9;
|
|
|
|
|
|
|
|
/* First try to fit the file size into 8 digits ... */
|
|
|
|
width = ulongcat(size, NULL, stp->st_size, width, ' ');
|
|
|
|
if (0 < width && width < sizeof(size)) {
|
|
|
|
/* ... if that is not enough expand the size buffer.
|
|
|
|
* This will only break the column alignment of the size
|
|
|
|
* attribute if really needed. */
|
|
|
|
ulongcat(size, NULL, stp->st_size, width, ' ');
|
|
|
|
}
|
|
|
|
|
|
|
|
add_to_string(string, size);
|
|
|
|
add_char_to_string(string, ' ');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
stat_date(struct string *string, struct stat *stp)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_STRFTIME
|
|
|
|
if (stp) {
|
|
|
|
time_t current_time = time(NULL);
|
|
|
|
time_t when = stp->st_mtime;
|
|
|
|
unsigned char *fmt;
|
|
|
|
|
|
|
|
if (current_time > when + 6L * 30L * 24L * 60L * 60L
|
|
|
|
|| current_time < when - 60L * 60L)
|
|
|
|
fmt = "%b %e %Y";
|
|
|
|
else
|
|
|
|
fmt = "%b %e %H:%M";
|
|
|
|
|
|
|
|
add_date_to_string(string, fmt, &when);
|
|
|
|
add_char_to_string(string, ' ');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
add_to_string(string, " ");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
compare_dir_entries(struct directory_entry *d1, struct directory_entry *d2)
|
|
|
|
{
|
|
|
|
if (d1->name[0] == '.' && d1->name[1] == '.' && !d1->name[2]) return -1;
|
|
|
|
if (d2->name[0] == '.' && d2->name[1] == '.' && !d2->name[2]) return 1;
|
|
|
|
if (d1->attrib[0] == 'd' && d2->attrib[0] != 'd') return -1;
|
|
|
|
if (d1->attrib[0] != 'd' && d2->attrib[0] == 'd') return 1;
|
|
|
|
return strcmp(d1->name, d2->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* This function decides whether a file should be shown in directory listing or
|
|
|
|
* not. Returns according boolean value. */
|
|
|
|
static inline int
|
|
|
|
file_visible(unsigned char *name, int get_hidden_files, int is_root_directory)
|
|
|
|
{
|
|
|
|
/* Always show everything not beginning with a dot. */
|
|
|
|
if (name[0] != '.')
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Always hide the "." directory. */
|
|
|
|
if (name[1] == '\0')
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Always show the ".." directory (but for root directory). */
|
|
|
|
if (name[1] == '.' && name[2] == '\0')
|
|
|
|
return !is_root_directory;
|
|
|
|
|
|
|
|
/* Others like ".x" or "..foo" are shown if get_hidden_files
|
|
|
|
* == 1. */
|
|
|
|
return get_hidden_files;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* First information such as permissions is gathered for each directory entry.
|
|
|
|
* All entries are then sorted and finally the sorted entries are added to the
|
|
|
|
* @data->fragment one by one. */
|
|
|
|
struct directory_entry *
|
|
|
|
get_directory_entries(unsigned char *dirname, int get_hidden)
|
|
|
|
{
|
|
|
|
struct directory_entry *entries = NULL;
|
|
|
|
DIR *directory;
|
|
|
|
int size = 0;
|
|
|
|
struct dirent *entry;
|
|
|
|
int is_root_directory = dirname[0] == '/' && !dirname[1];
|
|
|
|
|
|
|
|
directory = opendir(dirname);
|
|
|
|
if (!directory) return NULL;
|
|
|
|
|
|
|
|
while ((entry = readdir(directory))) {
|
|
|
|
struct stat st, *stp;
|
|
|
|
struct directory_entry *new_entries;
|
|
|
|
unsigned char *name;
|
|
|
|
struct string attrib;
|
|
|
|
|
|
|
|
if (!file_visible(entry->d_name, get_hidden, is_root_directory))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
new_entries = mem_realloc(entries, (size + 2) * sizeof(*new_entries));
|
|
|
|
if (!new_entries) continue;
|
|
|
|
entries = new_entries;
|
|
|
|
|
|
|
|
/* We allocate the full path because it is used in a few places
|
|
|
|
* which means less allocation although a bit more short term
|
|
|
|
* memory usage. */
|
|
|
|
name = straconcat(dirname, entry->d_name, NULL);
|
|
|
|
if (!name) continue;
|
|
|
|
|
|
|
|
if (!init_string(&attrib)) {
|
|
|
|
mem_free(name);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef FS_UNIX_SOFTLINKS
|
|
|
|
stp = (lstat(name, &st)) ? NULL : &st;
|
|
|
|
#else
|
|
|
|
stp = (stat(name, &st)) ? NULL : &st;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
stat_type(&attrib, stp);
|
|
|
|
stat_mode(&attrib, stp);
|
|
|
|
stat_links(&attrib, stp);
|
|
|
|
stat_user(&attrib, stp);
|
|
|
|
stat_group(&attrib, stp);
|
|
|
|
stat_size(&attrib, stp);
|
|
|
|
stat_date(&attrib, stp);
|
|
|
|
|
|
|
|
entries[size].name = name;
|
|
|
|
entries[size].attrib = attrib.source;
|
|
|
|
size++;
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir(directory);
|
|
|
|
|
|
|
|
if (!size) {
|
|
|
|
/* We may have allocated space for entries but added none. */
|
|
|
|
mem_free_if(entries);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
qsort(entries, size, sizeof(*entries),
|
|
|
|
(int (*)(const void *, const void *)) compare_dir_entries);
|
|
|
|
|
|
|
|
memset(&entries[size], 0, sizeof(*entries));
|
|
|
|
|
|
|
|
return entries;
|
|
|
|
}
|
2006-01-04 15:43:42 -05:00
|
|
|
|
2006-01-05 04:49:05 -05:00
|
|
|
/* Recursively create directories in a path. The last element in the path is
|
|
|
|
* taken to be a filename, and simply ignored */
|
|
|
|
int
|
|
|
|
mkalldirs(const unsigned char *path)
|
2006-01-04 15:43:42 -05:00
|
|
|
{
|
2006-01-05 04:49:05 -05:00
|
|
|
int pos, len, ret = 0;
|
|
|
|
unsigned char *p;
|
2006-01-04 15:43:42 -05:00
|
|
|
|
2006-01-05 04:49:05 -05:00
|
|
|
if (!*path) return -1;
|
2006-01-04 15:43:42 -05:00
|
|
|
|
2006-01-05 04:49:05 -05:00
|
|
|
/* Make a copy of path, to be able to write to it. Otherwise, the
|
|
|
|
* function is unsafe if called with a read-only char *argument. */
|
|
|
|
len = strlen(path) + 1;
|
|
|
|
p = fmem_alloc(len);
|
|
|
|
if (!p) return -1;
|
|
|
|
memcpy(p, path, len);
|
2006-01-04 15:43:42 -05:00
|
|
|
|
2006-01-05 04:49:05 -05:00
|
|
|
for (pos = 1; p[pos]; pos++) {
|
|
|
|
unsigned char separator = p[pos];
|
2006-01-04 15:43:42 -05:00
|
|
|
|
2006-01-05 04:49:05 -05:00
|
|
|
if (!dir_sep(separator))
|
|
|
|
continue;
|
2006-01-04 15:43:42 -05:00
|
|
|
|
2006-01-05 04:49:05 -05:00
|
|
|
p[pos] = 0;
|
2006-01-04 15:43:42 -05:00
|
|
|
|
2006-01-10 17:40:39 -05:00
|
|
|
ret = mkdir(p, S_IRWXU);
|
2006-01-04 15:43:42 -05:00
|
|
|
|
2006-01-05 04:49:05 -05:00
|
|
|
p[pos] = separator;
|
2006-01-04 15:43:42 -05:00
|
|
|
|
2006-01-05 04:49:05 -05:00
|
|
|
if (ret < 0 && errno != EEXIST)
|
|
|
|
break;
|
2006-01-04 15:43:42 -05:00
|
|
|
}
|
|
|
|
|
2006-01-05 04:49:05 -05:00
|
|
|
fmem_free(p);
|
2006-01-04 15:43:42 -05:00
|
|
|
return ret;
|
|
|
|
}
|