/* unix_files.c -- Built-in file handler functions for Unix-like files Copyright (C) 1998 John Harper $Id: unix_files.c,v 1.23 2002/07/14 18:25:00 jsh Exp $ This file is part of Jade. Jade is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. Jade is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Jade; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #define _GNU_SOURCE #include "repint.h" #include #include #include #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #if HAVE_DIRENT_H # include # define NAMLEN(dirent) strlen((dirent)->d_name) #else # define dirent direct # define NAMLEN(dirent) (dirent)->d_namlen # if HAVE_SYS_NDIR_H # include # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # endif #endif #ifndef PATH_MAX # define PATH_MAX 256 #endif #ifndef S_ISLNK #define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) #endif #ifndef S_ISSOCK #define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) #endif /* Support functions */ DEFSTRING(dot, "."); static inline u_char * file_part(char *name) { char *tmp = strrchr(name, '/'); return tmp != 0 ? tmp + 1 : name; } static struct stat * stat_file(repv file) { static struct stat statbuf; if(stat(rep_STR(file), &statbuf) == 0) return &statbuf; else return 0; } u_long rep_file_length(repv file) { struct stat *st = stat_file(file); if(st != 0) return st->st_size; else return 0; } /* File ops */ repv rep_file_name_absolute_p(repv file) { return (((rep_STR(file)[0] == '/') || (rep_STR(file)[0] == '~')) ? Qt : Qnil); } repv rep_expand_file_name(repv file) { char buf[PATH_MAX]; char *optr = buf; char *iptr = rep_STR(file); while(*iptr != 0) { char *end; if(iptr[0] == '.') { if(iptr[1] == '/') { iptr += 1; goto strip; } else if(iptr[1] == 0) { if(optr == buf) /* Only character in string. Must preserve the dot. */ *optr++ = '.'; iptr++; continue; } else if(iptr[1] == '.' && (iptr[2] == '/' || iptr[2] == 0)) { /* `XXX/..[/]' Try to back up over the parent directory */ char *back = optr; rep_bool all_dots = rep_TRUE; char *end; /* Step over any contiguous `/' characters */ while(back > buf && back[-1] == '/') back--; end = back; /* Step over any non-`/' characters */ while(back > buf && back[-1] != '/') { back--; if (back[0] != '.') all_dots = rep_FALSE; } if(back < optr && back >= buf && *back != '/' /* Don't allow `../..' -> `' */ && (!all_dots || end - back != 2)) { /* Reset the output ptr to the end of the parent */ optr = back; } /* Check for `/..' */ else if (all_dots && end == back && back == buf && optr > buf && buf[0] == '/' && optr - end == 1) { optr = back + 1; } else { /* Can't move up; leave the .. in the file name */ *optr++ = '.'; *optr++ = '.'; if(iptr[2] == '/') *optr++ = '/'; } iptr += (iptr[2] == 0) ? 2 : 3; goto strip; } } end = strchr(iptr, '/'); if(end == 0) end = iptr + strlen(iptr); memcpy(optr, iptr, end - iptr); optr += end - iptr; iptr = end; if(*iptr == '/') *optr++ = *iptr++; strip: /* merge multiple slashes into one */ while (*iptr && *iptr == '/') iptr++; } /* Don't allow a fully-empty string to be returned */ if (optr - buf == 0) *optr++ = '.'; if(optr - buf != rep_STRING_LEN(file) || memcmp(rep_STR(file), buf, optr - buf) != 0) return rep_string_dupn(buf, optr - buf); else return file; } repv rep_canonical_file_name(repv file) { char buf[PATH_MAX]; int len; if(realpath(rep_STR(file), buf) == 0) { /* realpath () failed; copy the source */ strncpy (buf, rep_STR (file), sizeof (buf)); } len = strlen(buf); while (len > 0 && buf[len - 1] == '/') { buf[len - 1] = 0; len--; } return rep_string_dupn(buf, len); } repv rep_file_name_nondirectory(repv file) { u_char *tem = file_part(rep_STR(file)); return tem == rep_STR(file) ? file : rep_string_dup(tem); } repv rep_file_name_directory(repv file) { int len = file_part(rep_STR(file)) - rep_STR(file); return rep_string_dupn(rep_STR(file), len); } repv rep_file_name_as_directory(repv file) { int len = rep_STRING_LEN(file); if(file_part(rep_STR(file)) == rep_STR(file) + len) { /* It's already a directory */ return file; } else { repv new = rep_string_dupn(rep_STR(file), len + 1); if(new) { rep_STR(new)[len] = '/'; rep_STR(new)[len+1] = 0; } return new; } } repv rep_directory_file_name(repv file) { int len = rep_STRING_LEN(file); if(file_part(rep_STR(file)) != rep_STR(file) + len) { /* There's a file part. Just return the initial string? */ return file; } else { if(len == 0) return rep_VAL(&dot); else if(len == 1) return file; else /* Chop the trailing "/" */ return rep_string_dupn(rep_STR(file), len - 1); } } repv rep_delete_file(repv file) { if(unlink(rep_STR(file)) == 0) return Qt; else return rep_signal_file_error(file); } repv rep_rename_file(repv old, repv new) { if(rename(rep_STR(old), rep_STR(new)) != -1) return Qt; else return rep_signal_file_error(rep_list_2(old, new)); } repv rep_make_directory(repv dir) { if(mkdir(rep_STR(dir), S_IRWXU | S_IRWXG | S_IRWXO) == 0) return Qt; else return rep_signal_file_error(dir); } repv rep_delete_directory(repv dir) { if(rmdir(rep_STR(dir)) == 0) return Qt; else return rep_signal_file_error(dir); } repv rep_copy_file(repv src, repv dst) { repv res = Qt; int srcf; srcf = open(rep_STR(src), O_RDONLY); if(srcf != -1) { int dstf = open(rep_STR(dst), O_WRONLY | O_CREAT | O_TRUNC, 0666); if(dstf != -1) { struct stat statb; int rd; if(fstat(srcf, &statb) == 0) chmod(rep_STR(dst), statb.st_mode); do { u_char buf[BUFSIZ]; int wr; rd = read(srcf, buf, BUFSIZ); if(rd < 0) { res = rep_signal_file_error(src); break; } wr = write(dstf, buf, rd); if(wr != rd) { res = rep_signal_file_error(dst); break; } } while(rd != 0); close(dstf); } else res = rep_signal_file_error(dst); close(srcf); } else res = rep_signal_file_error(src); return res; } repv rep_file_readable_p(repv file) { return access(rep_STR(file), R_OK) == 0 ? Qt : Qnil; } repv rep_file_writable_p(repv file) { return access(rep_STR(file), W_OK) == 0 ? Qt : Qnil; } repv rep_file_exists_p(repv file) { return access(rep_STR(file), F_OK) == 0 ? Qt : Qnil; } repv rep_file_regular_p(repv file) { struct stat *st = stat_file(file); if(st != 0) return S_ISREG(st->st_mode) ? Qt : Qnil; else return Qnil; } repv rep_file_directory_p(repv file) { struct stat *st = stat_file(file); if(st != 0) return S_ISDIR(st->st_mode) ? Qt : Qnil; else return Qnil; } repv rep_file_symlink_p(repv file) { struct stat st; if(lstat(rep_STR(file), &st) == 0) return S_ISLNK(st.st_mode) ? Qt : Qnil; else return Qnil; } repv rep_file_owner_p(repv file) { struct stat *st = stat_file(file); if(st != 0) return ((st->st_uid == geteuid() && st->st_gid == getegid()) ? Qt : Qnil); else return Qnil; } repv rep_file_nlinks(repv file) { struct stat *st = stat_file(file); if(st != 0) return rep_MAKE_INT(st->st_nlink); else return Qnil; } repv rep_file_size(repv file) { struct stat *st = stat_file(file); if(st != 0) return rep_make_long_uint(st->st_size); else return Qnil; } repv rep_file_modes(repv file) { struct stat *st = stat_file(file); if(st != 0) return rep_MAKE_INT(st->st_mode & 07777); else return Qnil; } repv rep_set_file_modes(repv file, repv modes) { rep_DECLARE2(modes, rep_INTP); if(chmod(rep_STR(file), rep_INT(modes)) == 0) return modes; else return rep_signal_file_error(file); } repv rep_file_modes_as_string(repv file) { struct stat *st = stat_file(file); repv string = Fmake_string(rep_MAKE_INT(10), rep_MAKE_INT('-')); if(st != 0 && string && rep_STRINGP(string)) { u_long perms = st->st_mode; int i; char c = '-'; if(S_ISDIR(perms)) c = 'd'; else if(S_ISLNK(perms)) c = 'l'; else if(S_ISBLK(perms)) c = 'b'; else if(S_ISCHR(perms)) c = 'c'; else if(S_ISFIFO(perms)) c = 'p'; else if(S_ISSOCK(perms)) c = 's'; rep_STR(string)[0] = c; for(i = 0; i < 3; i++) { u_long xperms = perms >> ((2 - i) * 3); if(xperms & 4) rep_STR(string)[1+i*3] = 'r'; if(xperms & 2) rep_STR(string)[2+i*3] = 'w'; c = (xperms & 1) ? 'x' : 0; if(perms & (04000 >> i)) { static char extra_bits[3] = { 'S', 'S', 'T' }; /* Rampant abuse of ASCII knowledge :-) */ c = extra_bits[i] | (c & 0x20); } if(c != 0) rep_STR(string)[3+i*3] = c; } } return string; } repv rep_file_modtime(repv file) { struct stat *st = stat_file(file); if(st != 0) return rep_MAKE_TIME(st->st_mtime); else /* Really this should return nil */ return rep_MAKE_TIME(0); } repv rep_directory_files(repv dir_name) { DIR *dir; if(*rep_STR(dir_name) == 0) dir_name = rep_VAL(&dot); dir = opendir(rep_STR(dir_name)); if(dir) { repv list = Qnil; struct dirent *de; while((de = readdir(dir))) { repv name = rep_string_dupn(de->d_name, NAMLEN(de)); list = Fcons(name, list); if(name == rep_NULL || list == rep_NULL) { rep_mem_error(); closedir(dir); return rep_NULL; } } closedir(dir); return list; } return Fsignal(Qfile_error, rep_list_2(rep_lookup_errno(), dir_name)); } repv rep_read_symlink (repv file) { char buf[PATH_MAX]; int len = readlink (rep_STR(file), buf, sizeof (buf)); if (len == -1) return rep_signal_file_error (file); else return rep_string_dupn (buf, len); } repv rep_make_symlink (repv file, repv contents) { if (symlink (rep_STR (contents), rep_STR (file)) == 0) return Qt; else return rep_signal_file_error (file); } repv rep_getpwd(void) { char buf[PATH_MAX]; #ifdef HAVE_GETCWD if(!getcwd(buf, PATH_MAX)) #else if(!getwd(buf)) #endif return rep_signal_file_error(Qnil); else { /* Ensure that it ends with "/" */ int len = strlen(buf); if(len < (PATH_MAX - 1) && buf[len] != '/') { buf[len++] = '/'; buf[len] = 0; } return rep_string_dupn(buf, len); } } /* module name conversion */ repv rep_structure_file (repv in) { /* Convert dots to slashes. XXX escape meta chars? */ char *ptr = strchr (rep_STR (in), '.'); if (ptr == 0) return in; else { repv copy = rep_string_dupn (rep_STR (in), rep_STRING_LEN (in)); for (ptr = rep_STR (copy); *ptr != 0; ptr++) { if (*ptr == '.') *ptr = '/'; } return copy; } }