home *** CD-ROM | disk | FTP | other *** search
- /* file.c -- Copyright (1988) Dan Heller */
-
- #include "mush.h"
- #include <pwd.h>
-
- /* takes string 'p' and address of int (isdir). If p uses the ~ to reference
- * a home directory of some sort, then expand it. find out what sort of
- * file final path is. set isdir to 1 if a directory, 0 if not, -1 on error
- * return final path. If an error occurs, return string indicating error.
- * if isdir has a value of 1 when passed, it ignores "No such file or directory"
- */
- char *
- getpath(p, isdir)
- register char *p;
- int *isdir;
- {
- static char buf[MAXPATHLEN];
- struct stat stat_buf;
-
- if (!p || !*p || !strcmp(p, "~")) {
- char *home = do_set(set_options, "home");
- if (!home || !*home)
- home = ALTERNATE_HOME;
- (void) strcpy(buf, home); /* no arg means home */
- } else if (*p == '~') {
- if (p[1] != '/') {
- /* not our home, but someone else's
- * look for ~user or ~user/subpath
- * if '/' exists, separate into tmp="user" p="subpath"
- */
- struct passwd *ent, *getpwnam();
- char *p2 = p+1;
- if (p = index(p2, '/'))
- *p++ = 0;
- if (!(ent = getpwnam(p2))) {
- *isdir = -1;
- return sprintf(buf, "no such user: %s", p2);
- }
- /* append subpath to pathname */
- if (p && *p)
- (void) sprintf(buf, "%s/%s", ent->pw_dir, p);
- /* if *p == NULL, pathname is done (buf), set isdir = 1 */
- else {
- *isdir = 1;
- return strcpy(buf, ent->pw_dir);
- }
- } else {
- char *home = do_set(set_options, "home");
- if (!home || !*home)
- home = ALTERNATE_HOME;
- (void) sprintf(buf, "%s/%s", home, p+2);
- }
- } else if (*p == '%') {
- /* if %user, append user name... else, it's just us */
- if (!*++p || *p == ' ' || *p == '\t')
- (void) strcpy(buf, spoolfile);
- else
- #ifndef HOMEMAIL
- (void) sprintf(buf, "%s/%s", MAILDIR, p);
- #else /* HOMEMAIL */
- {
- /* If it's NOT us, recur to get the path for ~user/MAILFILE */
- int t_isdir = *isdir;
- char *t, tmp[MAXPATHLEN];
- (void) sprintf(tmp, "~%s/%s", p, MAILFILE);
- t = getpath(tmp, &t_isdir);
- if (t_isdir == -1) {
- *isdir = -1;
- return t;
- }
- /* strcpy(buf, t); --buf already has info because it's static */
- }
- #endif /* HOMEMAIL */
- } else if (*p == '+') {
- register char *p2 = do_set(set_options, "folder");
- if (!p2 || !*p2)
- p2 = DEF_FOLDER;
- if (*++p)
- (void) sprintf(buf, "%s/%s", p2, p);
- else
- (void) strcpy(buf, p2);
- if (*buf != '/') {
- int t_isdir = *isdir;
- char *t, tmp[MAXPATHLEN];
- if (*buf != '~')
- (void) sprintf(tmp, "~/%s", buf);
- else
- (void) strcpy(tmp, buf);
- t = getpath(tmp, &t_isdir);
- if (t_isdir == -1) {
- *isdir = -1;
- return t;
- }
- /* strcpy(buf, t); --buf already has info because it's static */
- }
- } else { /* allow \ to escape the special chars, +, %, ~ */
- if (*p == '\\')
- p++;
- (void) strcpy(buf, p);
- }
- if (stat(buf, &stat_buf)) {
- (void) access(buf, F_OK); /* set errno to the "real" reason */
- if (errno == ENOENT && *isdir == 1) {
- *isdir = 0; /* say it's a regular file even tho it doesn't exist */
- return buf; /* it may be wanted for creating */
- }
- *isdir = -1;
- return sys_errlist[errno];
- }
- *isdir = ((stat_buf.st_mode & S_IFMT) == S_IFDIR);
- return buf;
- }
-
- /*
- * Given a (possibly NULL or empty) string, return the name of a a valid
- * directory. The string may contain the usual filename metachars (see
- * above). Returns the current user's home directory if the input string
- * does not refer to a directory, the ALTERNATE_HOME if the user's home
- * directory cannot be found, or NULL if none of the above are accessible.
- *
- * NOTE: Returns the getpath() static buffer, so the same caveats apply.
- */
- char *
- getdir(path)
- char *path;
- {
- int isdir = 0;
-
- /* getpath() already handles the NULL and empty cases */
- if (!(path = getpath(path, &isdir)) || isdir != 1) {
- isdir = 0;
- path = getpath(ALTERNATE_HOME, &isdir);
- if (isdir != 1)
- path = NULL;
- }
- return path;
- }
-
- /*
- * Given a filename[pointer] (p), a file pointer, and a mode, file_to_fp
- * opens the file with the mode.
- * If the mode is "r" then we read the file into the file pointer at the
- * end (fseek(fp, 2, 0)). If the file is opened for writing, then read
- * from the beginning of fp and write it into the file.
- * This is usually called to read .signatures into messages (thus,
- * opening .signature with "r" and writing to the end of fp which is probably
- * the sendmail process or the message file pointer) or to write fortunes into
- * the message buffer: reading fp (the popened fortune) and writing into file.
- */
- file_to_fp(p, fp, mode)
- register char *p;
- register FILE *fp;
- char *mode;
- {
- int x = 1;
- char *file, buf[BUFSIZ];
- FILE *tmp_fp;
-
- if (!p || !*p) {
- print("specify filename");
- return -1;
- }
- /* Special case for IS_SENDING && !IS_GETTING should eventually go away */
- if (ison(glob_flags, IS_SENDING) && isoff(glob_flags, IS_GETTING) &&
- strcmp(p, "-") == 0) {
- file = p;
- if (*mode == 'r')
- tmp_fp = stdin;
- else
- tmp_fp = stdout;
- } else {
- file = getpath(p, &x);
- if (x == -1) { /* on error, file contains error message */
- wprint(file);
- return -1;
- }
- wprint("%s: ", file);
- if (x) {
- /* if x == 1, then path is a directory */
- wprint("is a directory.\n");
- return -1;
- } else if (!(tmp_fp = fopen(file, mode))) {
- wprint("%s\n", sys_errlist[errno]);
- return -1;
- }
- }
- if (*mode != 'r') {
- rewind(fp);
- for(x = 0; fgets(buf, BUFSIZ, fp); x++)
- (void) fputs(buf, tmp_fp);
- } else {
- for(x = 0; fgets(buf, BUFSIZ, tmp_fp); x++)
- (void) fputs(buf, fp);
- (void) fflush(fp);
- }
- wprint("%s%d line%s\n", (*mode == 'a')? "added ": "",
- x, (x == 1)? "": "s");
- if (file != p || strcmp(file, "-") != 0)
- (void) fclose(tmp_fp);
- return 0;
- }
-
- /* clear all contents of the file. Careful that the file is opened for
- * _writing_ --tempfile is opened for reading, so don't try to empty it
- * if you're using ftruncate. Return -1 on error, 0 on success.
- */
- emptyfile(fp, fname)
- register FILE **fp;
- register char *fname;
- {
- Debug("Emptying \"%s\"\n", fname);
- #ifndef SYSV
- return ftruncate(fileno(*fp), 0L);
- #else
- {
- int omask = umask(077), ret;
- (void) fclose(*fp);
- if (!(*fp = fopen(fname, "w")))
- ret = -1;
- ret = 0;
- (void) umask(omask);
- return ret;
- }
- #endif /* SYSV */
- }
-
- /*
- * Finds out how many file descriptors are opened. Useful for making sure
- * no files got opened in subprocedures which were not subsequently closed.
- * If argc is 0, returns the number of available fds.
- */
- nopenfiles(argc)
- {
- #ifdef MAXFILES
- register int size = MAXFILES;
- #else
- register int size = getdtablesize();
- #endif /* MAXFILES */
- register int nfiles = 0, totalfiles = size;
-
- if (argc > 1)
- return -1;
-
- if (argc == 1)
- wprint("open file descriptors:");
- while (--size >= 0)
- if (fcntl(size, F_GETFL, 0) != -1) {
- if (argc == 1)
- wprint(" %d", size);
- ++nfiles;
- }
- if (argc == 1) {
- wprint("\n");
- return 0;
- }
- return totalfiles - nfiles;
- }
-
- /*
- * Close all "extraneous" file descriptors; return the number closed
- */
- closefileds (n)
- {
- register int nfiles = 0;
- #ifdef MAXFILES
- register int size = MAXFILES;
- #else
- register int size = getdtablesize();
- #endif /* MAXFILES */
-
- while (--size >= n)
- if (fcntl(size, F_GETFL, 0) != -1) {
- (void) close(size);
- ++nfiles;
- }
- return nfiles;
- }
-
- /*
- * Open a path for writing or appending -- return a FILE pointer.
- * If program is TRUE, then use popen, not fopen and don't check
- * to see if the file is writable. If program is FALSE and lockit
- * is TRUE, then lock on open.
- */
- FILE *
- open_file(p, program, lockit)
- register char *p;
- {
- register FILE *newfile = NULL_FILE;
- register char *tmp;
- int x = 1;
-
- if (program || *p == '/')
- tmp = p, x = 0;
- else
- tmp = getpath(p, &x);
- if (x == 1)
- print("%s is a directory.\n", tmp);
- else if (x == -1)
- print("%s: %s\n", p, tmp);
- else {
- register char *mode = NULL;
- /* if it doesn't exist open for "w" */
- if (program || Access(tmp, F_OK))
- mode = "w";
- /* if we can't write to it, forget it */
- else if (Access(tmp, W_OK))
- error(tmp);
- else
- mode = "a";
- if (mode)
- if (program) {
- if (!(newfile = popen(tmp, mode)))
- error("Can't execute %s\n", tmp);
- } else if (lockit) {
- /* Lock on open */
- if (!(newfile = lock_fopen(tmp, mode)))
- error("Can't write to %s", tmp);
- } else {
- /* Ordinary open */
- if (!(newfile = mask_fopen(tmp, mode)))
- error("Can't write to %s", tmp);
- }
- if (newfile != NULL_FILE)
- Debug("Successfully opened %s\n", tmp);
- }
- return newfile;
- }
-
- /*
- * Open each file in the vector names[] and place the corresponding
- * file descriptor in files[]. If the file is really a program (pipe),
- * delete the name after opening; otherwise lock the file.
- * Tokens beginning with a "/, ~, or + are files; tokens beginning
- * with a | are programs.
- */
- open_list(names, files, size)
- char *names[];
- FILE *files[];
- {
- register int total = 0, prog;
- register char *fpath;
-
- Debug("opening "), print_argv(names);
- for (total = 0; size && total < size; ) {
- fpath = names[total] + (prog = (names[total][0] == '|'));
- /* open_file() locks the file here only if prog is false */
- if ((files[total] = open_file(fpath, prog, TRUE))) {
- if (prog) {
- xfree(names[total]);
- names[total++] = NULL;
- } else {
- /* Seek to end of file AFTER locking */
- (void) fseek(files[total++], 0L, 2);
- }
- } else {
- Debug("Failed to open %s\n", names[total]);
- /* Swap the failed file with the last in the list */
- if (size--) {
- xfree(names[total]);
- names[total] = names[size];
- names[size] = NULL;
- }
- }
- }
- return size;
- }
-
- /*
- * find_files gets a set of addresses and an array of
- * char pointers and the maximum size that array can be.
- * The object is to find the files or programs listed in "s". If the
- * size is 0, then just extract the file names and give error messages
- * for each one since they will not be opened. Return the number of
- * files found and delete all files from the list in * "s".
- * The string "s" is modified to be a list of address -- all names AND
- * files are stripped out of the list.
- * The force parameter causes names to be interpreted as files even if
- * they would normally appear to be addresses.
- */
- find_files(s, names, size, force)
- register char *s;
- char *names[];
- {
- register int total = 0;
- char file[MAXPATHLEN], buf[HDRSIZ], *start = s, c;
- register char *p, *b = buf, *fpath;
-
- do {
- if (!(p = get_name_n_addr(s, NULL, file)))
- break;
- c = *p, *p = 0;
- /* See if it's a file. This doesn't get written back
- * onto "buf" since it is supposed to be extracted anyway.
- */
- if (force || *file == '+' || *file == '~' ||
- *file == '|' || *file == '/') {
- int isdir;
- /* open either "file" or &file[1] */
- if (*file == '|') {
- isdir = 0;
- fpath = file;
- } else {
- isdir = 1;
- /* if successful, getpath will reset isdir to 0 */
- fpath = getpath(file, &isdir);
- }
- if (!isdir) {
- if (size && total < size)
- names[total++] = savestr(fpath);
- else
- print("No open space for %s\n", file);
- } else if (isdir == 1)
- print("%s: is a directory\n", file);
- else
- print("%s: %s\n", file, fpath);
- } else {
- b += Strcpy(b, s);
- *b++ = ',', *b++ = ' ';
- }
- for (*p = c, s = p; *s == ',' || isspace(*s); s++)
- ;
- } while (*s);
- for (*b-- = 0; b > buf && (*b == ',' || isspace(*b)); b--)
- *b = 0;
- (void) strcpy(start, buf);
- names[total] = NULL; /* for free_vec() */
- return total;
- }
-
- /*
- * access(2) has an undocumented feature which ignores suid. If you are
- * su'ed and try to read your mail, you will be unable to because access()
- * will give the illusion that you cannot read/write to your mbox. Solve
- * the problem by using stat() instead.
- */
- Access(file, mode)
- register char *file;
- {
- struct stat buf;
-
- if (stat(file, &buf) == -1)
- return -1;
- if (mode == R_OK)
- return (buf.st_mode & 0400)? 0 : -1;
- if (mode == W_OK)
- return (buf.st_mode & 0200)? 0 : -1;
- return 0;
- }
-
- /*
- * Open a file for read/write/whatever but make sure umask is rw by user only.
- */
- FILE *
- mask_fopen(file, mode)
- char *file, *mode;
- {
- int omask = umask(077);
- FILE *fp = fopen(file, mode);
- (void) umask(omask);
- return fp;
- }
-
- /*
- * Shorten a file name, replacing its full path name with one using an
- * accepted mush abbreviation:
- * ~ home directory
- * + folder directory
- * For files in the current directory, the path is simply skipped.
- * Returns a pointer into a static buffer holding the trimmed path.
- */
- char *
- trim_filename(name)
- char *name;
- {
- static char buf[MAXPATHLEN];
- char *fldr = do_set(set_options, "folder"),
- *home = do_set(set_options, "home");
- int len;
-
- /* Handling $folder is tough, because if it is not set then we should
- * trim DEF_FOLDER; but DEF_FOLDER may not be a full path, and we can't
- * call getpath() because the "name" parameter may point to gepath()'s
- * static buffer. So we handle the special case of DEF_FOLDER starting
- * with a tilde ($home), and forget about it otherwise. Yuck.
- */
- if ((!fldr || !*fldr) && (fldr = DEF_FOLDER) && *fldr == '~' && home) {
- (void) sprintf(buf, "%s%s", home, fldr + 1);
- fldr = buf; /* buf will get overwritten again below */
- }
- /* One more special case: if $folder and $home are the same, then we
- * trim as $home, otherwise we trim as $folder. This prevents strange
- * contractions like "+.cshrc" for "~/.cshrc".
- */
- if ((!home || strcmp(home, fldr)) && (len = strlen(fldr)) &&
- !strncmp(fldr, name, len) && (name[len] == '/' || !name[len])) {
- buf[0] = '+';
- if (name[len] && name[len + 1])
- (void) strcpy(buf + 1, name + len + 1);
- else
- buf[1] = 0;
- return buf;
- } else if (home && (len = strlen(home)) && !strncmp(home, name, len) &&
- (name[len] == '/' || !name[len])) {
- buf[0] = '~';
- (void) strcpy(buf + 1, name + len);
- return buf;
- } else if ((fldr = do_set(set_options, "cwd")) &&
- (len = strlen(fldr)) && !strncmp(fldr, name, len) &&
- name[len] == '/')
- return strcpy(buf, name + len + 1);
- return strcpy(buf, name);
- }
-