home *** CD-ROM | disk | FTP | other *** search
/ ftp.freefriends.org / ftp.freefriends.org.tar / ftp.freefriends.org / arnold / Source / mush.rstevens.tar.gz / mush.tar / file.c < prev    next >
C/C++ Source or Header  |  1992-10-30  |  15KB  |  543 lines

  1. /* file.c -- Copyright (1988) Dan Heller */
  2.  
  3. #include "mush.h"
  4. #include <pwd.h>
  5.  
  6. #ifdef SYSV
  7. #ifdef EWOULDBLOCK
  8. #undef EWOULDBLOCK
  9. #endif /* EWOULDBLOCK */
  10. #define EWOULDBLOCK     EAGAIN
  11. #endif /* SYSV */
  12.  
  13. /* takes string 'p' and address of int (isdir).  If p uses the ~ to reference
  14.  * a home directory of some sort, then expand it.  find out what sort of
  15.  * file final path is. set isdir to 1 if a directory, 0 if not, -1 on error
  16.  * return final path. If an error occurs, return string indicating error.
  17.  * if isdir has a value of 1 when passed, it ignores "No such file or directory"
  18.  */
  19. char *
  20. getpath(p, isdir)
  21. register char *p;
  22. int *isdir;
  23. {
  24.     static char buf[MAXPATHLEN];
  25.     struct stat stat_buf;
  26.  
  27.     if (p != buf) { /* Just in case */
  28.     if (!p || !*p || !strcmp(p, "~")) {
  29.         char *home = do_set(set_options, "home");
  30.         if (!home || !*home)
  31.         home = ALTERNATE_HOME;
  32.         (void) strcpy(buf, home);  /* no arg means home */
  33.     } else if (*p == '~') {
  34.         if (p[1] != '/') {
  35.         /* not our home, but someone else's
  36.          * look for ~user or ~user/subpath
  37.          * if '/' exists, separate into tmp="user" p="subpath"
  38.          */
  39.         struct passwd *ent, *getpwnam();
  40.         char *p2 = p+1;
  41.         if (p = index(p2, '/'))
  42.             *p++ = 0;
  43.         if (!(ent = getpwnam(p2))) {
  44.             *isdir = -1;
  45.             return sprintf(buf, "no such user: %s", p2);
  46.         }
  47.         /* append subpath to pathname */
  48.         if (p && *p)
  49.             (void) sprintf(buf, "%s/%s", ent->pw_dir, p);
  50.         /* if *p == NULL, pathname is done (buf), set isdir = 1 */
  51.         else {
  52.             *isdir = 1;
  53.             return strcpy(buf, ent->pw_dir);
  54.         }
  55.         } else {
  56.         char *home = do_set(set_options, "home");
  57.         if (!home || !*home)
  58.             home = ALTERNATE_HOME;
  59.         (void) sprintf(buf, "%s/%s", home, p+2);
  60.         }
  61.     } else if (*p == '%') {
  62.         /* if %user, append user name... else, it's just us */
  63.         if (!*++p || *p == ' ' || *p == '\t')
  64.         (void) strcpy(buf, spoolfile);
  65.         else
  66. #ifndef HOMEMAIL
  67.         (void) sprintf(buf, "%s/%s", MAILDIR, p);
  68. #else /* HOMEMAIL */
  69.         {
  70.         /* If it's NOT us, recur to get the path for ~user/MAILFILE */
  71.         int t_isdir = *isdir;
  72.         char *t, tmp[MAXPATHLEN];
  73.         (void) sprintf(tmp, "~%s/%s", p, MAILFILE);
  74.         t = getpath(tmp, &t_isdir);
  75.         if (t_isdir == -1) {
  76.             *isdir = -1;
  77.             return t;
  78.         }
  79.         /* strcpy(buf, t); --buf already has info because it's static */
  80.         }
  81. #endif /* HOMEMAIL */
  82.     } else if (*p == '+') {
  83.         register char *p2 = do_set(set_options, "folder");
  84.         if (!p2 || !*p2)
  85.         p2 = DEF_FOLDER;
  86.         if (*++p)
  87.         (void) sprintf(buf, "%s/%s", p2, p);
  88.         else
  89.         (void) strcpy(buf, p2);
  90.         if (*buf != '/') {
  91.         int t_isdir = *isdir;
  92.         char *t, tmp[MAXPATHLEN];
  93.         if (*buf != '~')
  94.             (void) sprintf(tmp, "~/%s", buf);
  95.         else
  96.             (void) strcpy(tmp, buf);
  97.         t = getpath(tmp, &t_isdir);
  98.         if (t_isdir == -1) {
  99.             *isdir = -1;
  100.             return t;
  101.         }
  102.         /* strcpy(buf, t); --buf already has info because it's static */
  103.         }
  104.     } else {  /* allow \ to escape the special chars, +, %, ~ */
  105.         if (*p == '\\')
  106.         p++;
  107.         (void) strcpy(buf, p);
  108.     }
  109.     }
  110.     if (stat(buf, &stat_buf)) {
  111.     (void) access(buf, F_OK); /* set errno to the "real" reason */
  112.     if (errno == ENOENT && *isdir == 1) {
  113.         *isdir = 0; /* say it's a regular file even tho it doesn't exist */
  114.         return buf; /* it may be wanted for creating */
  115.     }
  116.     *isdir = -1;
  117.     return sys_errlist[errno];
  118.     }
  119.     *isdir = ((stat_buf.st_mode & S_IFMT) == S_IFDIR);
  120.     return buf;
  121. }
  122.  
  123. /*
  124.  * Given a (possibly NULL or empty) string, return the name of a a valid
  125.  * directory.  The string may contain the usual filename metachars (see
  126.  * above).  Returns the current user's home directory if the input string
  127.  * does not refer to a directory, the ALTERNATE_HOME if the user's home
  128.  * directory cannot be found, or NULL if none of the above are accessible.
  129.  *
  130.  * NOTE:  Returns the getpath() static buffer, so the same caveats apply.
  131.  */
  132. char *
  133. getdir(path)
  134. char *path;
  135. {
  136.     int isdir = 0;
  137.  
  138.     /* getpath() already handles the NULL and empty cases */
  139.     if (!(path = getpath(path, &isdir)) || isdir != 1) {
  140.     isdir = 0;
  141.     path = getpath(ALTERNATE_HOME, &isdir);
  142.     if (isdir != 1)
  143.         path = NULL;
  144.     }
  145.     return path;
  146. }
  147.  
  148. /*
  149.  * Given a filename[pointer] (p), a file pointer, and a mode, file_to_fp
  150.  * opens the file with the mode.
  151.  * If the mode is "r" then we read the file into the file pointer at the
  152.  * end (fseek(fp, 2, 0)).  If the file is opened for writing, then read
  153.  * from the beginning of fp and write it into the file.
  154.  * This is usually called to read .signatures into messages (thus,
  155.  * opening .signature with "r" and writing to the end of fp which is probably
  156.  * the sendmail process or the message file pointer) or to write fortunes into
  157.  * the message buffer: reading fp (the popened fortune) and writing into file.
  158.  */
  159. file_to_fp(p, fp, mode)
  160. register char *p;
  161. register FILE *fp;
  162. char *mode;
  163. {
  164.     int     x = 1;
  165.     char     *file, buf[BUFSIZ];
  166.     FILE     *tmp_fp;
  167.  
  168.     if (!p || !*p) {
  169.     print("specify filename");
  170.     return -1;
  171.     }
  172.     /* Special case for IS_SENDING && !IS_GETTING should eventually go away */
  173.     if (ison(glob_flags, IS_SENDING) && isoff(glob_flags, IS_GETTING) &&
  174.         strcmp(p, "-") == 0) {
  175.     file = p;
  176.     if (*mode == 'r')
  177.         tmp_fp = stdin;
  178.     else
  179.         tmp_fp = stdout;
  180.     } else {
  181.     file = getpath(p, &x);
  182.     if (x == -1) { /* on error, file contains error message */
  183.         wprint(file);
  184.         return -1;
  185.     }
  186.     wprint("%s: ", file);
  187.     if (x) {
  188.         /* if x == 1, then path is a directory */
  189.         wprint("is a directory.\n");
  190.         return -1;
  191.     } else if (!(tmp_fp = fopen(file, mode))) {
  192.         wprint("%s\n", sys_errlist[errno]);
  193.         return -1;
  194.     }
  195.     }
  196.     if (*mode != 'r') {
  197.     rewind(fp);
  198.     for(x = 0; fgets(buf, BUFSIZ, fp); x++)
  199.         (void) fputs(buf, tmp_fp);
  200.     } else {
  201.     for(x = 0; fgets(buf, BUFSIZ, tmp_fp); x++)
  202.         (void) fputs(buf, fp);
  203.     (void) fflush(fp);
  204.     }
  205.     wprint("%s%d line%s\n", (*mode == 'a')? "added ": "",
  206.                   x, (x == 1)? "": "s");
  207.     if (file != p || strcmp(file, "-") != 0)
  208.     (void) fclose(tmp_fp);
  209.     return 0;
  210. }
  211.  
  212. /* clear all contents of the file.  Careful that the file is opened for
  213.  * _writing_ --tempfile is opened for reading, so don't try to empty it
  214.  * if you're using ftruncate.   Return -1 on error, 0 on success.
  215.  */
  216. emptyfile(fp, fname)
  217. register FILE **fp;
  218. register char *fname;
  219. {
  220.     Debug("Emptying \"%s\"\n", fname);
  221. #ifndef SYSV
  222.     return ftruncate(fileno(*fp), 0L);
  223. #else
  224.     {
  225.     int omask = umask(077), ret;
  226.     (void) fclose(*fp);
  227.     if (!(*fp = fopen(fname, "w")))
  228.         ret = -1;
  229.     else
  230.         ret = 0;
  231.     (void) umask(omask);
  232.     return ret;
  233.     }
  234. #endif /* SYSV */
  235. }
  236.  
  237. /*
  238.  * Finds out how many file descriptors are opened.  Useful for making sure
  239.  * no files got opened in subprocedures which were not subsequently closed.
  240.  * If argc is 0, returns the number of available fds.
  241.  */
  242. nopenfiles(argc)
  243. {
  244. #ifdef MAXFILES
  245.     register int size = MAXFILES;
  246. #else
  247.     register int size = getdtablesize();
  248. #endif /* MAXFILES */
  249.     register int nfiles = 0, totalfiles = size;
  250.  
  251.     if (argc > 1)
  252.     return -1;
  253.  
  254.     if (argc == 1)
  255.     wprint("open file descriptors:");
  256.     while (--size >= 0)
  257.     if (fcntl(size, F_GETFL, 0) != -1) {
  258.         if (argc == 1)
  259.         wprint(" %d", size);
  260.         ++nfiles;
  261.     }
  262.     if (argc == 1) {
  263.     wprint("\n");
  264.     return 0;
  265.     }
  266.     return totalfiles - nfiles;
  267. }
  268.  
  269. /*
  270.  * Close all "extraneous" file descriptors; return the number closed
  271.  */
  272. closefileds (n)
  273. {
  274.     register int nfiles = 0;
  275. #ifdef MAXFILES
  276.     register int size = MAXFILES;
  277. #else
  278.     register int size = getdtablesize();
  279. #endif /* MAXFILES */
  280.  
  281.     while (--size >= n)
  282.     if (fcntl(size, F_GETFL, 0) != -1) {
  283.         (void) close(size);
  284.         ++nfiles;
  285.     }
  286.     return nfiles;
  287. }
  288.  
  289. /*
  290.  * Open a path for writing or appending -- return a FILE pointer.
  291.  * If program is TRUE, then use popen, not fopen and don't check 
  292.  * to see if the file is writable.  If program is FALSE and lockit
  293.  * is TRUE, then lock on open.
  294.  */
  295. FILE *
  296. open_file(p, program, lockit)
  297. register char *p;
  298. {
  299.     register FILE *newfile = NULL_FILE;
  300.     register char *tmp;
  301.     int x = 1;
  302.  
  303.     if (program)
  304.     tmp = p, x = 0;
  305.     else
  306.     tmp = getpath(p, &x);
  307.     if (x == 1)
  308.     print("%s is a directory.\n", tmp);
  309.     else if (x == -1)
  310.     print("%s: %s\n", p, tmp);
  311.     else {
  312.     register char *mode = NULL;
  313.     /* if it doesn't exist open for "w" */
  314.     if (program || Access(tmp, F_OK))
  315.         mode = "w";
  316.     /* if we can't write to it, forget it */
  317.     else if (Access(tmp, W_OK))
  318.         error(tmp);
  319.     else
  320.         mode = "a";
  321.     if (mode)
  322.         if (program) {
  323.         if (!(newfile = popen(tmp, mode)))
  324.             error("Can't execute %s\n", tmp);
  325.         } else if (lockit) {
  326.         /* Lock on open */
  327.         if (!(newfile = lock_fopen(tmp, mode)) && errno != EWOULDBLOCK)
  328.             error("Can't write to %s", tmp);
  329.         } else {
  330.         /* Ordinary open */
  331.         if (!(newfile = mask_fopen(tmp, mode)))
  332.             error("Can't write to %s", tmp);
  333.         }
  334.         if (newfile != NULL_FILE)
  335.         Debug("Successfully opened %s\n", tmp);
  336.     }
  337.     return newfile;
  338. }
  339.  
  340. /*
  341.  * Open each file in the vector names[] and place the corresponding
  342.  * file descriptor in files[].  If the file is really a program (pipe),
  343.  * delete the name after opening; otherwise lock the file.
  344.  * Tokens beginning with a "/, ~, or + are files; tokens beginning
  345.  * with a | are programs.
  346.  */
  347. open_list(names, files, size)
  348. char *names[];
  349. FILE *files[];
  350. {
  351.     register int total = 0, prog;
  352.     register char *fpath;
  353.  
  354.     Debug("opening "), print_argv(names);
  355.     for (total = 0; size && total < size; ) {
  356.     fpath = names[total] + (prog = (names[total][0] == '|'));
  357.     /* open_file() locks the file here only if prog is false */
  358.     if ((files[total] = open_file(fpath, prog, TRUE))) {
  359.         if (prog) {
  360.         xfree(names[total]);
  361.         names[total++] = NULL;
  362.         } else {
  363.         /* Seek to end of file AFTER locking */
  364.         (void) fseek(files[total++], 0L, 2);
  365.         }
  366.     } else {
  367.         Debug("Failed to open %s\n", names[total]);
  368.         /* Swap the failed file with the last in the list */
  369.         if (size--) {
  370.         xfree(names[total]);
  371.         names[total] = names[size];
  372.         names[size] = NULL;
  373.         }
  374.     }
  375.     }
  376.     return size;
  377. }
  378.  
  379. /*
  380.  * find_files gets a set of addresses and an array of
  381.  * char pointers and the maximum size that array can be.
  382.  * The object is to find the files or programs listed in "s".  If the
  383.  * size is 0, then just extract the file names and give error messages
  384.  * for each one since they will not be opened. Return the number of
  385.  * files found and delete all files from the list in * "s".
  386.  * The string "s" is modified to be a list of address -- all names AND
  387.  * files are stripped out of the list.
  388.  * The force parameter causes names to be interpreted as files even if
  389.  * they would normally appear to be addresses.
  390.  */
  391. find_files(s, names, size, force)
  392. register char *s;
  393. char *names[];
  394. {
  395.     register int     total = 0;
  396.     char          file[MAXPATHLEN], buf[HDRSIZ], *start = s, c;
  397.     register char    *p, *b = buf, *fpath;
  398.  
  399.     do  {
  400.     if (!(p = get_name_n_addr(s, NULL, file)))
  401.         break;
  402.     c = *p, *p = 0;
  403.     /* See if it's a file.  This doesn't get written back
  404.      * onto "buf" since it is supposed to be extracted anyway.
  405.      * The check for '=' in names beginning with '/' is to
  406.      * avoid mis-identifying X.400 addresses as file names.
  407.      *
  408.      * \052 is a * for broken compilers that would do a comment.
  409.      */
  410.     if (force || *file == '+' || *file == '~' ||
  411.         *file == '|' || *file == '/' && !glob(file, "/?*=*?/\052")) {
  412.         int isdir;
  413.         /* open either "file" or &file[1] */
  414.         if (*file == '|') {
  415.         isdir = 0;
  416.         fpath = file;
  417.         } else {
  418.         isdir = 1;
  419.         /* if successful, getpath will reset isdir to 0 */
  420.         fpath = getpath(file, &isdir);
  421.         }
  422.         if (!isdir) {
  423.         if (size && total < size)
  424.             names[total++] = savestr(fpath);
  425.         else
  426.             print("No open space for %s\n", file);
  427.         } else if (isdir == 1)
  428.         print("%s: is a directory\n", file);
  429.         else
  430.         print("%s: %s\n", file, fpath);
  431.     } else {
  432.         b += Strcpy(b, s);
  433.         *b++ = ',', *b++ = ' ';
  434.     }
  435.     for (*p = c, s = p; *s == ',' || isspace(*s); s++)
  436.         ;
  437.     } while (*s);
  438.     for (*b-- = 0; b > buf && (*b == ',' || isspace(*b)); b--)
  439.     *b = 0;
  440.     (void) strcpy(start, buf);
  441.     names[total] = NULL; /* for free_vec() */
  442.     return total;
  443. }
  444.  
  445. /*
  446.  * access(2) has an undocumented feature which ignores suid.  If you are
  447.  * su'ed and try to read your mail, you will be unable to because access()
  448.  * will give the illusion that you cannot read/write to your mbox.  Solve
  449.  * the problem by using stat() instead.
  450.  */
  451. Access(file, mode)
  452. register char *file;
  453. {
  454.     struct stat buf;
  455.  
  456.     if (stat(file, &buf) == -1)
  457.     return -1;
  458.     if (mode == R_OK)
  459.     return (buf.st_mode & 0400)? 0 : -1;
  460.     if (mode == W_OK)
  461.     return (buf.st_mode & 0200)? 0 : -1;
  462.     return 0;
  463. }
  464.  
  465. /*
  466.  * Open a file for read/write/whatever but make sure umask is rw by user only.
  467.  */
  468. FILE *
  469. mask_fopen(file, mode)
  470. char *file, *mode;
  471. {
  472.     int omask = umask(077);
  473.     FILE *fp;
  474. #ifdef SYSV
  475.     /* XENIX and other older sytems can't handle "a+".    Even newer
  476.      * SysV systems define a+ such that all writes go at end-of-file,
  477.      * not at whatever the current seek position is.  Good grief.
  478.      */
  479.     if (strcmp(mode, "a+") == 0) {
  480.     if (Access(file, F_OK) == 0)
  481.         mode = "r+";
  482.     else
  483.         mode = "w+";
  484.     if (fp = fopen(file, mode))
  485.         (void) fseek(fp, 0L, 2); /* assure we're at the end of the file */
  486.     } else
  487. #endif /* SYSV */
  488.     fp = fopen(file, mode);
  489.     (void) umask(omask);
  490.     return fp;
  491. }
  492.  
  493. /*
  494.  * Shorten a file name, replacing its full path name with one using an
  495.  *  accepted mush abbreviation:
  496.  *    ~    home directory
  497.  *    +    folder directory
  498.  *  For files in the current directory, the path is simply skipped.
  499.  * Returns a pointer into a static buffer holding the trimmed path.
  500.  */
  501. char *
  502. trim_filename(name)
  503. char *name;
  504. {
  505.     static char buf[MAXPATHLEN];
  506.     char *fldr = do_set(set_options, "folder"),
  507.      *home = do_set(set_options, "home");
  508.     int len;
  509.  
  510.     /* Handling $folder is tough, because if it is not set then we should
  511.      * trim DEF_FOLDER; but DEF_FOLDER may not be a full path, and we can't
  512.      * call getpath() because the "name" parameter may point to gepath()'s
  513.      * static buffer.  So we handle the special case of DEF_FOLDER starting
  514.      * with a tilde ($home), and forget about it otherwise.  Yuck.
  515.      */
  516.     if ((!fldr || !*fldr) && (fldr = DEF_FOLDER) && *fldr == '~' && home) {
  517.     (void) sprintf(buf, "%s%s", home, fldr + 1);
  518.     fldr = buf;  /* buf will get overwritten again below */
  519.     }
  520.     /* One more special case: if $folder and $home are the same, then we
  521.      * trim as $home, otherwise we trim as $folder.  This prevents strange
  522.      * contractions like "+.cshrc" for "~/.cshrc".
  523.      */
  524.     if ((!home || strcmp(home, fldr)) && (len = strlen(fldr)) &&
  525.         !strncmp(fldr, name, len) && (name[len] == '/' || !name[len])) {
  526.     buf[0] = '+';
  527.     if (name[len] && name[len + 1])
  528.         (void) strcpy(buf + 1, name + len + 1);
  529.     else
  530.         buf[1] = 0;
  531.     return buf;
  532.     } else if (home && (len = strlen(home)) && !strncmp(home, name, len) &&
  533.         (name[len] == '/' || !name[len])) {
  534.     buf[0] = '~';
  535.     (void) strcpy(buf + 1, name + len);
  536.     return buf;
  537.     } else if ((fldr = do_set(set_options, "cwd")) &&
  538.         (len = strlen(fldr)) && !strncmp(fldr, name, len) &&
  539.         name[len] == '/')
  540.     return strcpy(buf, name + len + 1);
  541.     return strcpy(buf, name);
  542. }
  543.