/* * Utility routines. * * Copyright (C) 1998 by Erik Andersen * * 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; either version 2 of the License, or * (at your option) any later version. * * 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 * * Based in part on code from sash, Copyright (c) 1999 by David I. Bell * Permission has been granted to redistribute this code under the GPL. * */ #include "internal.h" #include #include #include #include #include #include #include #include #include #if 0 extern char *join_paths(char *buffer, const char *a, const char *b) { int length = 0; if (a && *a) { length = strlen(a); memcpy(buffer, a, length); } if (b && *b) { if (length > 0 && buffer[length - 1] != '/') buffer[length++] = '/'; if (*b == '/') b++; strcpy(&buffer[length], b); } return buffer; } #endif static CHUNK *chunkList; /* * Return the standard ls-like mode string from a file mode. * This is static and so is overwritten on each call. */ const char *modeString(int mode) { static char buf[12]; strcpy(buf, "----------"); /* * Fill in the file type. */ if (S_ISDIR(mode)) buf[0] = 'd'; if (S_ISCHR(mode)) buf[0] = 'c'; if (S_ISBLK(mode)) buf[0] = 'b'; if (S_ISFIFO(mode)) buf[0] = 'p'; #ifdef S_ISLNK if (S_ISLNK(mode)) buf[0] = 'l'; #endif #ifdef S_ISSOCK if (S_ISSOCK(mode)) buf[0] = 's'; #endif /* * Now fill in the normal file permissions. */ if (mode & S_IRUSR) buf[1] = 'r'; if (mode & S_IWUSR) buf[2] = 'w'; if (mode & S_IXUSR) buf[3] = 'x'; if (mode & S_IRGRP) buf[4] = 'r'; if (mode & S_IWGRP) buf[5] = 'w'; if (mode & S_IXGRP) buf[6] = 'x'; if (mode & S_IROTH) buf[7] = 'r'; if (mode & S_IWOTH) buf[8] = 'w'; if (mode & S_IXOTH) buf[9] = 'x'; /* * Finally fill in magic stuff like suid and sticky text. */ if (mode & S_ISUID) buf[3] = ((mode & S_IXUSR) ? 's' : 'S'); if (mode & S_ISGID) buf[6] = ((mode & S_IXGRP) ? 's' : 'S'); if (mode & S_ISVTX) buf[9] = ((mode & S_IXOTH) ? 't' : 'T'); return buf; } /* * Return TRUE if a fileName is a directory. * Nonexistant files return FALSE. */ int isDirectory(const char *name) { struct stat statBuf; if (stat(name, &statBuf) < 0) return FALSE; return S_ISDIR(statBuf.st_mode); } /* * Return TRUE if a filename is a block or character device. * Nonexistant files return FALSE. */ int isDevice(const char *name) { struct stat statBuf; if (stat(name, &statBuf) < 0) return FALSE; return S_ISBLK(statBuf.st_mode) || S_ISCHR(statBuf.st_mode); } /* * Copy one file to another, while possibly preserving its modes, times, * and modes. Returns TRUE if successful, or FALSE on a failure with an * error message output. (Failure is not indicted if the attributes cannot * be set.) */ int copyFile( const char *srcName, const char *destName, int setModes, int followLinks) { int rfd; int wfd; int rcc; char buf[BUF_SIZE]; struct stat statBuf1; struct stat statBuf2; struct utimbuf times; if (stat(srcName, &statBuf1) < 0) { perror(srcName); return FALSE; } if (stat(destName, &statBuf2) < 0) { statBuf2.st_ino = -1; statBuf2.st_dev = -1; } if ((statBuf1.st_dev == statBuf2.st_dev) && (statBuf1.st_ino == statBuf2.st_ino)) { fprintf(stderr, "Copying file \"%s\" to itself\n", srcName); return FALSE; } if (S_ISDIR(statBuf1.st_mode)) { /* Make sure the directory is writable */ if (mkdir(destName, 0777777 ^ umask(0))) { perror(destName); return (FALSE); } } else if (S_ISFIFO(statBuf1.st_mode)) { if (mkfifo(destName, 644)) { perror(destName); return (FALSE); } } else if (S_ISBLK(statBuf1.st_mode) || S_ISCHR(statBuf1.st_mode)) { if (mknod(destName, 644, statBuf1.st_rdev)) { perror(destName); return (FALSE); } } else if (S_ISLNK(statBuf1.st_mode)) { char *link_val; int link_size; link_val = (char *) alloca(PATH_MAX + 2); link_size = readlink(srcName, link_val, PATH_MAX + 1); if (link_size < 0) { perror(srcName); return (FALSE); } link_val[link_size] = '\0'; if (symlink(link_val, destName)) { perror(srcName); return (FALSE); } } else { rfd = open(srcName, O_RDONLY); if (rfd < 0) { perror(srcName); return FALSE; } wfd = creat(destName, statBuf1.st_mode); if (wfd < 0) { perror(destName); close(rfd); return FALSE; } while ((rcc = read(rfd, buf, sizeof(buf))) > 0) { if (fullWrite(wfd, buf, rcc) < 0) goto error_exit; } if (rcc < 0) { perror(srcName); goto error_exit; } close(rfd); if (close(wfd) < 0) { perror(destName); return FALSE; } } if (setModes == TRUE) { chmod(destName, statBuf1.st_mode); if (followLinks == TRUE) chown(destName, statBuf1.st_uid, statBuf1.st_gid); else lchown(destName, statBuf1.st_uid, statBuf1.st_gid); times.actime = statBuf1.st_atime; times.modtime = statBuf1.st_mtime; utime(destName, ×); } return TRUE; error_exit: close(rfd); close(wfd); return FALSE; } /* * Build a path name from the specified directory name and file name. * If the directory name is NULL, then the original fileName is returned. * The built path is in a static area, and is overwritten for each call. */ char *buildName(const char *dirName, const char *fileName) { const char *cp; static char buf[PATH_LEN]; if ((dirName == NULL) || (*dirName == '\0')) { strcpy(buf, fileName); return buf; } cp = strrchr(fileName, '/'); if (cp) fileName = cp + 1; strcpy(buf, dirName); strcat(buf, "/"); return buf; } /* * Make a NULL-terminated string out of an argc, argv pair. * Returns TRUE if successful, or FALSE if the string is too long, * with an error message given. This does not handle spaces within * arguments correctly. */ int makeString( int argc, const char **argv, char *buf, int bufLen) { int len; while (argc-- > 0) { len = strlen(*argv); if (len >= bufLen) { fprintf(stderr, "Argument string too long\n"); return FALSE; } strcpy(buf, *argv++); buf += len; bufLen -= len; if (argc) *buf++ = ' '; bufLen--; } *buf = '\0'; return TRUE; } /* * Allocate a chunk of memory (like malloc). * The difference, though, is that the memory allocated is put on a * list of chunks which can be freed all at one time. You CAN NOT free * an individual chunk. */ char *getChunk(int size) { CHUNK *chunk; if (size < CHUNK_INIT_SIZE) size = CHUNK_INIT_SIZE; chunk = (CHUNK *) malloc(size + sizeof(CHUNK) - CHUNK_INIT_SIZE); if (chunk == NULL) return NULL; chunk->next = chunkList; chunkList = chunk; return chunk->data; } /* * Duplicate a string value using the chunk allocator. * The returned string cannot be individually freed, but can only be freed * with other strings when freeChunks is called. Returns NULL on failure. */ char *chunkstrdup(const char *str) { int len; char *newStr; len = strlen(str) + 1; newStr = getChunk(len); if (newStr) memcpy(newStr, str, len); return newStr; } /* * Free all chunks of memory that had been allocated since the last * call to this routine. */ void freeChunks(void) { CHUNK *chunk; while (chunkList) { chunk = chunkList; chunkList = chunk->next; free((char *) chunk); } } /* * Get the time string to be used for a file. * This is down to the minute for new files, but only the date for old files. * The string is returned from a static buffer, and so is overwritten for * each call. */ const char *timeString(time_t timeVal) { time_t now; char *str; static char buf[26]; time(&now); str = ctime(&timeVal); strcpy(buf, &str[4]); buf[12] = '\0'; if ((timeVal > now) || (timeVal < now - 365 * 24 * 60 * 60L)) { strcpy(&buf[7], &str[20]); buf[11] = '\0'; } return buf; } /* * Routine to see if a text string is matched by a wildcard pattern. * Returns TRUE if the text is matched, or FALSE if it is not matched * or if the pattern is invalid. * * matches zero or more characters * ? matches a single character * [abc] matches 'a', 'b' or 'c' * \c quotes character c * Adapted from code written by Ingo Wilken. */ int match(const char *text, const char *pattern) { const char *retryPat; const char *retryText; int ch; int found; retryPat = NULL; retryText = NULL; while (*text || *pattern) { ch = *pattern++; switch (ch) { case '*': retryPat = pattern; retryText = text; break; case '[': found = FALSE; while ((ch = *pattern++) != ']') { if (ch == '\\') ch = *pattern++; if (ch == '\0') return FALSE; if (*text == ch) found = TRUE; } if (!found) { pattern = retryPat; text = ++retryText; } /* fall into next case */ case '?': if (*text++ == '\0') return FALSE; break; case '\\': ch = *pattern++; if (ch == '\0') return FALSE; /* fall into next case */ default: if (*text == ch) { if (*text) text++; break; } if (*text) { pattern = retryPat; text = ++retryText; break; } return FALSE; } if (pattern == NULL) return FALSE; } return TRUE; } /* * Write all of the supplied buffer out to a file. * This does multiple writes as necessary. * Returns the amount written, or -1 on an error. */ int fullWrite(int fd, const char *buf, int len) { int cc; int total; total = 0; while (len > 0) { cc = write(fd, buf, len); if (cc < 0) return -1; buf += cc; total += cc; len -= cc; } return total; } /* * Read all of the supplied buffer from a file. * This does multiple reads as necessary. * Returns the amount read, or -1 on an error. * A short read is returned on an end of file. */ int fullRead(int fd, char *buf, int len) { int cc; int total; total = 0; while (len > 0) { cc = read(fd, buf, len); if (cc < 0) return -1; if (cc == 0) break; buf += cc; total += cc; len -= cc; } return total; } /* * Walk down all the directories under the specified * location, and do something (something specified * by the fileAction and dirAction function pointers). */ int recursiveAction(const char *fileName, int recurse, int followLinks, int (*fileAction) (const char *fileName), int (*dirAction) (const char *fileName)) { int status; struct stat statbuf; struct dirent *next; if (followLinks) status = lstat(fileName, &statbuf); else status = stat(fileName, &statbuf); if (status < 0) { perror(fileName); return (FALSE); } if (recurse == FALSE) { if (S_ISDIR(statbuf.st_mode)) { if (dirAction == NULL) return (TRUE); else return (dirAction(fileName)); } else { if (fileAction == NULL) return (TRUE); else return (fileAction(fileName)); } } if (S_ISDIR(statbuf.st_mode)) { DIR *dir; fprintf(stderr, "Dir: %s\n", fileName); dir = opendir(fileName); if (!dir) { perror(fileName); return (FALSE); } if (dirAction != NULL) { status = dirAction(fileName); if (status == FALSE) { perror("cp"); return (FALSE); } } while ((next = readdir(dir)) != NULL) { char nextFile[NAME_MAX]; if ((strcmp(next->d_name, "..") == 0) || (strcmp(next->d_name, ".") == 0)) { continue; } sprintf(nextFile, "%s/%s", fileName, next->d_name); status = recursiveAction(nextFile, TRUE, followLinks, fileAction, dirAction); if (status < 0) { closedir(dir); return (FALSE); } } status = closedir(dir); if (status < 0) { perror(fileName); return (FALSE); } } else { fprintf(stderr, "File: %s\n", fileName); if (fileAction == NULL) return (TRUE); else return (fileAction(fileName)); } return (TRUE); } /* END CODE */