home *** CD-ROM | disk | FTP | other *** search
/ ftp.cs.arizona.edu / ftp.cs.arizona.edu.tar / ftp.cs.arizona.edu / icon / historic / v941.tgz / icon.v941src.tar / icon.v941src / src / common / munix.c < prev    next >
C/C++ Source or Header  |  2002-01-15  |  7KB  |  252 lines

  1. /*  munix.c -- special common code for Unix  */
  2.  
  3. #include "../h/gsupport.h"
  4.  
  5. #if UNIX
  6.  
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <sys/stat.h>
  11. #include <sys/types.h>
  12. #include <unistd.h>
  13.  
  14. static char *findexe(char *name, char *buf, size_t len);
  15. static char *findonpath(char *name, char *buf, size_t len);
  16. static char *followsym(char *name, char *buf, size_t len);
  17. static char *canonize(char *path);
  18.  
  19. /*
  20.  *  relfile(prog, mod) -- find related file.
  21.  *
  22.  *  Given that prog is the argv[0] by which this program was executed,
  23.  *  and assuming that it was set by the shell or other equally correct
  24.  *  invoker, relfile finds the location of a related file and returns
  25.  *  it in an allocated string.  It takes the location of prog, appends
  26.  *  mod, and canonizes the result; thus if argv[0] is icont or its path,
  27.  *  relfile(argv[0],"/../iconx") finds the location of iconx.
  28.  */
  29. char *relfile(char *prog, char *mod) {
  30.    static char baseloc[MaxPath];
  31.    char buf[MaxPath];
  32.  
  33.    if (baseloc[0] == 0) {        /* if argv[0] not already found */
  34.       if (findexe(prog, baseloc, sizeof(baseloc)) == NULL) {
  35.          fprintf(stderr, "cannot find location of %s\n", prog);
  36.          exit(EXIT_FAILURE);
  37.          }
  38.       if (followsym(baseloc, buf, sizeof(buf)) != NULL)
  39.          strcpy(baseloc, buf);
  40.    }
  41.  
  42.    strcpy(buf, baseloc);        /* start with base location */
  43.    strcat(buf, mod);            /* append adjustment */
  44.    canonize(buf);            /* canonize result */
  45.    if (mod[strlen(mod)-1] == '/')    /* if trailing slash wanted */
  46.       strcat(buf, "/");            /* append to result */
  47.    return salloc(buf);            /* return allocated string */
  48.    }
  49.  
  50. /*
  51.  *  findexe(prog, buf, len) -- find absolute executable path, given argv[0]
  52.  *
  53.  *  Finds the absolute path to prog, assuming that prog is the value passed
  54.  *  by the shell in argv[0].  The result is placed in buf, which is returned.
  55.  *  NULL is returned in case of error.
  56.  */
  57.  
  58. static char *findexe(char *name, char *buf, size_t len) {
  59.    int i, n;
  60.    char *s, *t;
  61.    char tbuf[MaxPath];
  62.  
  63.    if (name == NULL)
  64.       return NULL;
  65.  
  66.    /* if name does not contain a slash, search $PATH for file */
  67.    if (strchr(name, '/') != NULL)
  68.       strcpy(buf, name);
  69.    else if (findonpath(name, buf, len) == NULL)
  70.       return NULL;
  71.  
  72.    /* if path is not absolute, prepend working directory */
  73.    if (buf[0] != '/') {
  74.       n = strlen(buf) + 1;
  75.       memmove(buf + len - n, buf, n);
  76.       if (getcwd(buf, len - n) == NULL)
  77.          return NULL;
  78.       s = buf + strlen(buf);
  79.       *s = '/';
  80.       memcpy(s + 1, buf + len - n, n);
  81.       }
  82.    canonize(buf);
  83.    return buf;
  84.    }
  85.  
  86. /*
  87.  *  findonpath(name, buf, len) -- find name on $PATH
  88.  *
  89.  *  Searches $PATH (using POSIX 1003.2 rules) for executable name,
  90.  *  writing the resulting path in buf if found.
  91.  */
  92. static char *findonpath(char *name, char *buf, size_t len) {
  93.    int nlen, plen;
  94.    char *path, *next, *sep, *end;
  95.    struct stat status;
  96.  
  97.    nlen = strlen(name);
  98.    path = getenv("PATH");
  99.    if (path == NULL || *path == '\0')
  100.       path = ".";
  101.    end = path + strlen(path);
  102.    for (next = path; next <= end; next = sep + 1) {
  103.       sep = strchr(next, ':');
  104.       if (sep == NULL)
  105.          sep = end;
  106.       plen = sep - next;
  107.       if (plen == 0) {
  108.          next = ".";
  109.          plen = 1;
  110.          }
  111.       if (plen + 1 + nlen + 1 > len)
  112.          return NULL;
  113.       memcpy(buf, next, plen);
  114.       buf[plen] = '/';
  115.       strcpy(buf + plen + 1, name);
  116.       if (access(buf, X_OK) == 0) {
  117.          if (stat(buf, &status) == 0 && S_ISREG(status.st_mode))
  118.             return buf;
  119.          }
  120.       }
  121.    return NULL;
  122.    }
  123.  
  124. /*
  125.  *  followsym(name, buf, len) -- follow symlink to final destination.
  126.  *
  127.  *  If name specifies a file that is a symlink, resolves the symlink to
  128.  *  its ultimate destination, and returns buf.  Otherwise, returns NULL.
  129.  *
  130.  *  Note that symlinks in the path to name do not make it a symlink.
  131.  *
  132.  *  buf should be long enough to hold name.
  133.  */
  134.  
  135. #define MAX_FOLLOWED_LINKS 24
  136.  
  137. static char *followsym(char *name, char *buf, size_t len) {
  138.    int i, n;
  139.    char *s, tbuf[MaxPath];
  140.  
  141.    strcpy(buf, name);
  142.  
  143.    for (i = 0; i < MAX_FOLLOWED_LINKS; i++) {
  144.       if ((n = readlink(buf, tbuf, sizeof(tbuf) - 1)) <= 0)
  145.          break;
  146.       tbuf[n] = 0;
  147.  
  148.       if (tbuf[0] == '/') {
  149.          if (n < len)
  150.             strcpy(buf, tbuf);
  151.          else
  152.             return NULL;
  153.          }
  154.       else {
  155.          s = strrchr(buf, '/');
  156.          if (s != NULL)
  157.             s++;
  158.          else
  159.             s = buf;
  160.          if ((s - buf) + n < len)
  161.             strcpy(s, tbuf);
  162.          else
  163.             return NULL;
  164.          }
  165.       canonize(buf);
  166.       }
  167.  
  168.    if (i > 0 && i < MAX_FOLLOWED_LINKS)
  169.       return buf;
  170.    else
  171.       return NULL;
  172.    }
  173.  
  174. /*
  175.  *  canonize(path) -- put file path in canonical form.
  176.  *
  177.  *  Rewrites path in place, and returns it, after excising fragments of
  178.  *  "." or "dir/..".  All leading slashes are preserved but other extra
  179.  *  slashes are deleted.  The path never grows longer except for the
  180.  *  special case of an empty path, which is rewritten to be ".".
  181.  *
  182.  *  No check is made that any component of the path actually exists or
  183.  *  that inner components are truly directories.  From this it follows
  184.  *  that if "foo" is any file path, canonizing "foo/.." produces the path
  185.  *  of the directory containing "foo".
  186.  */
  187.  
  188. static char *canonize(char *path) {
  189.    int len;
  190.    char *root, *end, *in, *out, *prev;
  191.  
  192.    /* initialize */
  193.    root = path;                /* set barrier for trimming by ".." */
  194.    end = path + strlen(path);        /* set end of input marker */
  195.    while (*root == '/')            /* preserve all leading slashes */
  196.       root++;
  197.    in = root;                /* input pointer */
  198.    out = root;                /* output pointer */
  199.  
  200.    /* scan string one component at a time */
  201.    while (in < end) {
  202.  
  203.       /* count component length */
  204.       for (len = 0; in + len < end && in[len] != '/'; len++)
  205.          ;
  206.  
  207.       /* check for ".", "..", or other */
  208.       if (len == 1 && *in == '.')    /* just ignore "." */
  209.          in++;
  210.       else if (len == 2 && in[0] == '.' && in[1] == '.') {
  211.          in += 2;            /* skip over ".." */
  212.          /* find start of previous component */
  213.          prev = out;
  214.          if (prev > root)
  215.             prev--;            /* skip trailing slash */
  216.          while (prev > root && prev[-1] != '/')
  217.             prev--;            /* find next slash or start of path */
  218.          if (prev < out - 1
  219.          && (out - prev != 3 || strncmp(prev, "../", 3) != 0)) {
  220.             out = prev;        /* trim trailing component */
  221.             }
  222.          else {
  223.             memcpy(out, "../", 3);    /* cannot trim, so must keep ".." */
  224.             out += 3;
  225.             }
  226.          }
  227.       else {
  228.          memmove(out, in, len);        /* copy component verbatim */
  229.          out += len;
  230.          in += len;
  231.          *out++ = '/';            /* add output separator */
  232.          }
  233.  
  234.       while (in < end && *in == '/')    /* consume input separators */
  235.          in++;
  236.       }
  237.  
  238.    /* final fixup */
  239.    if (out > root)
  240.       out--;                /* trim trailing slash */
  241.    if (out == path)
  242.       *out++ = '.';            /* change null path to "." */
  243.    *out++ = '\0';
  244.    return path;                /* return result */
  245.    }
  246.  
  247. #else                    /* UNIX */
  248.  
  249. static char junk;        /* avoid empty module */
  250.  
  251. #endif                    /* UNIX */
  252.