/* elmo - ELectronic Mail Operator Copyright (C) 2003, 2004 rzyjontko This program 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; version 2. This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ---------------------------------------------------------------------- file handling */ /**************************************************************************** * IMPLEMENTATION HEADERS ****************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #ifdef HAVE_MMAP # include #endif #include "xmalloc.h" #include "rstring.h" #include "file.h" #include "misc.h" #include "error.h" /**************************************************************************** * IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS ****************************************************************************/ #ifndef P_tmpdir # define P_tmpdir "/tmp" #endif /**************************************************************************** * IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID) ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE DATA ****************************************************************************/ /**************************************************************************** * INTERFACE DATA ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTIONS ****************************************************************************/ static int get_part (FILE *fp, int start, int end, char **place) { int len; if (fseek (fp, start, SEEK_SET)){ return 1; } len = end - start; *place = xmalloc (len + 1); if (fread (*place, 1, len, fp) != len){ xfree (*place); *place = NULL; return 1; } (*place)[len] = '\0'; return 0; } /**************************************************************************** * INTERFACE FUNCTIONS ****************************************************************************/ int file_part (const char *fname, int offset_start, int offset_end, char **place) { FILE *fp; int ret; if (place == NULL) return 1; *place = NULL; fp = fopen (fname, "r"); if (fp == NULL) return 1; ret = get_part (fp, offset_start, offset_end, place); fclose (fp); return ret; } int file_size (const char *fname) { struct stat st; if (stat (fname, &st)) return -1; return st.st_size; } int file_whole (FILE *fp, char **place, size_t *size) { struct stat st; int fd; rewind (fp); fd = fileno (fp); if (fstat (fd, &st)) return 1; *size = st.st_size; return get_part (fp, 0, st.st_size, place); } char * file_with_dir (const char *dir, const char *file) { size_t dlen = 0; size_t flen = 0; int need_sep = 1; char *result = NULL; if (file == NULL) return xstrdup (dir); if (dir == NULL) return xstrdup (file); dlen = strlen (dir); flen = strlen (file); if (*file == '/' || dir[dlen - 1] == '/') need_sep = 0; result = xmalloc (dlen + need_sep + flen + 1); memcpy (result, dir, dlen); memcpy (result + dlen + need_sep, file, flen); if (need_sep) result[dlen] = '/'; result[dlen + need_sep + flen] = '\0'; return result; } int file_temp_fd (char **temp_name) { char name[] = P_tmpdir "/elmo-XXXXXX"; int fd; while (1){ fd = mkstemp (name); if (fd != -1) break; if (errno != EEXIST){ error_ (errno, "%s", name); return -1; } } if (temp_name) *temp_name = xstrdup (name); return fd; } FILE * file_temp_file (char **temp_name) { int fd; fd = file_temp_fd (temp_name); if (fd == -1) return NULL; return fdopen (fd, "r+"); } FILE * file_open (char *fname, const char *mode, int flags, int access) { int fd; char *true_name; FILE *fp; true_name = file_expand_tilde (fname); fd = open (true_name, flags, access); if (fd == -1){ if (true_name != fname) xfree (true_name); return NULL; } fp = fdopen (fd, mode); if (true_name != fname) xfree (true_name); return fp; } #ifdef HAVE_MMAP int file_rename (const char *oldname, const char *newname) { struct stat st; int ofd; int nfd; char *ofile; char *nfile; if (rename (oldname, newname) == 0) return 0; if (errno != EXDEV) return 1; /** * step 0: open old file */ ofd = open (oldname, O_RDONLY); if (ofd == -1) return 1; /** * step 1: get old file size */ if (fstat (ofd, &st)){ close (ofd); return 1; } /** * step 2: open new file for writing */ nfd = open (newname, O_RDWR | O_CREAT, 0600); if (nfd == -1){ close (ofd); return 1; } /** * step 3: resize new file */ if (ftruncate (nfd, st.st_size)){ close (ofd); close (nfd); return 1; } /** * step 4: mmap both files */ ofile = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE, ofd, 0); if (ofile == MAP_FAILED){ close (ofd); close (nfd); return 1; } nfile = mmap (NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, nfd, 0); if (nfile == MAP_FAILED){ close (ofd); close (nfd); return 1; } /** * step 5: copy file contents */ memcpy (nfile, ofile, st.st_size); /** * step 6: unmap regions and close file descriptors */ munmap (ofile, st.st_size); munmap (nfile, st.st_size); close (ofd); close (nfd); unlink (oldname); return 0; } #else int file_rename (const char *oldname, const char *newname) { struct stat st; FILE *nfp; FILE *ofp; char *buf; int ret; if (rename (oldname, newname) == 0) return 0; if (errno != EXDEV) return 1; ofp = file_open (oldname, "r", O_RDONLY); if (ofp == NULL) return 1; nfp = file_open (newname, "w", O_WRONLY | O_CREAT | O_EXCL); if (nfp == NULL) return 1; buf = xmalloc (4096); while (! feof (ofp)){ ret = fread (buf, 1, 4096, ofp); if (fwrite (buf, 1, ret, nfp) != ret){ xfree (buf); fclose (ofp); fclose (nfp); return 1; } } xfree (buf); fclose (ofp); fclose (nfp); unlink (oldname); return 0; } #endif char * file_expand_tilde (char *dir) { char *result; char *home = getenv ("HOME"); int hlen = (home) ? strlen (home) : 0; int dlen = (dir) ? strlen (dir) : 0; if (dir == NULL) return NULL; if (*dir != '~') return dir; result = xmalloc (hlen + dlen); memcpy (result, home, hlen); memcpy (result + hlen, dir + 1, dlen); return result; } rstring_t * file_dir_items (const char *dir, int (*select)(const struct dirent *)) { int i; int len; int count; struct stat st; struct dirent **list; rstring_t *result; char *file; if (dir == NULL) return NULL; count = scandir (dir, &list, select, alphasort); if (count == -1){ return NULL; } result = rstring_create_size (count + 1); for (i = 0; i < count; i++){ file = file_with_dir (dir, list[i]->d_name); if (stat (file, &st)){ xfree (file); continue; } xfree (file); if (S_ISDIR (st.st_mode)){ len = strlen (list[i]->d_name); file = xmalloc (len + 2); memcpy (file, list[i]->d_name, len); file[len] = '/'; file[len + 1] = '\0'; } else { file = xstrdup (list[i]->d_name); } rstring_add (result, file); } result->allocated_all = 1; for (i = 0; i < count; i++) free (list[i]); if (count) free (list); return result; } /**************************************************************************** * INTERFACE CLASS BODIES ****************************************************************************/ /**************************************************************************** * * END MODULE file.c * ****************************************************************************/