shithub: femtolisp

ref: 8b59a493d6b5e51811321339450bddbe2aa56c24
dir: /llt/dirpath.c/

View raw version
#ifdef PLAN9
#include <u.h>
#include <libc.h>
#define getcwd getwd
#else
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/poll.h>
#include <unistd.h>
#endif

#include "dtypes.h"
#include "dirpath.h"

void get_cwd(char *buf, size_t size)
{
    getcwd(buf, size);
}

int set_cwd(char *buf)
{
    return chdir(buf);
}

// destructively convert path to directory part
void path_to_dirname(char *path)
{
    char *sep = strrchr(path, PATHSEP);
    if (sep != NULL) {
        *sep = '\0';
    }
    else {
        path[0] = '\0';
    }
}

#ifdef PLAN9
char *get_exename(char *buf, size_t size)
{
    snprint(buf, size, argv0);
    return buf;
}
#elif defined(LINUX)
char *get_exename(char *buf, size_t size)
{
    char linkname[64]; /* /proc/<pid>/exe */
    pid_t pid;
    ssize_t ret;

    /* Get our PID and build the name of the link in /proc */
    pid = getpid();

    if (snprintf(linkname, sizeof(linkname), "/proc/%i/exe", pid) < 0)
        return NULL;

    /* Now read the symbolic link */
    ret = readlink(linkname, buf, size);

    /* In case of an error, leave the handling up to the caller */
    if (ret == -1)
        return NULL;

    /* Report insufficient buffer size */
    if ((size_t)ret >= size)
        return NULL;

    /* Ensure proper NUL termination */
    buf[ret] = 0;

    return buf;
}
#elif defined(OPENBSD)
#include <sys/param.h>
#include <sys/sysctl.h>

char *get_exename(char *buf, size_t size)
{
    int mib[4];
    pid_t pid;
    size_t len, plen;
    char  **argv, **argv2;
    char *p, *path, *pathcpy, filename[PATH_MAX];
    struct stat sbuf;

    pid = getpid();

    mib[0] = CTL_KERN;
    mib[1] = KERN_PROC_ARGS;
    mib[2] = pid;
    mib[3] = KERN_PROC_ARGV;

    USED(size);
    buf = NULL;
    argv = NULL;
    len = 128;

    // Now, play The Guessing Game with sysctl(3) as to how big argv
    // is supposed to be. (It's loads of fun for the whole family!)

    while (len < SIZE_MAX / 2) {
        len *= 2;
        if ((argv2 = realloc(argv, len)) == NULL)
            break;
        argv = argv2;
        if (sysctl(mib, 4, argv, &len, NULL, 0) == -1) {
            if (errno == ENOMEM)
                continue; // Go back and realloc more memory.
            break; // Bail for some other error in sysctl(3).
        }
        // If you made it here, congrats! You guessed right!
        if (*argv != NULL)
            buf = strdup(*argv);
        break;
    }
    free(argv);

    // If no error occurred in the sysctl(3) KERN_PROC_ARGV call
    // above, then buf at this point contains some kind of pathname.

    if (buf != NULL) {
	if (strchr(buf, '/') == NULL) {
	    // buf contains a `basename`-style pathname (i.e. "foo",
	    // as opposed to "../foo" or "/usr/bin/foo"); search the
	    // PATH for its location. (BTW the setgid(2), setuid(2)
	    // calls are a pre-condition for the access(2) call
	    // later.)

	    if ( (path = getenv("PATH")) != NULL &&
		 !setgid(getegid()) && !setuid(geteuid()) ) {

		// The strdup(3) call below, if successful, will
		// allocate memory for the PATH string returned by
		// getenv(3) above.  This is necessary because the man
		// page of getenv(3) says that its return value
		// "should be considered read-only"; however, the
		// strsep(3) call below is going to be destructively
		// modifying that value. ("Hulk smash!")

		if ((path = strdup(path)) != NULL) {
		    pathcpy = path;
		    len = strlen(buf);
		    while ((p = strsep(&pathcpy, ":")) != NULL) {
			if (*p == '\0') p = ".";
			plen = strlen(p);

			// strip trailing '/'
			while (p[plen-1] == '/') p[--plen] = '\0';

			if (plen + 1 + len < sizeof(filename)) {
			    snprintf(filename, sizeof(filename), "%s/%s", p, buf);
			    if ( (stat(filename, &sbuf) == 0) &&
				 S_ISREG(sbuf.st_mode) &&
				 access(filename, X_OK) == 0 ) {
				buf = strdup(filename);
				break;
			    }
			}
		    }
		    free(path); // free the strdup(3) memory allocation.
		}
	    }
	    else buf = NULL; // call to getenv(3) or [sg]ete?[ug]id(2) failed.
	}
	if ( buf != NULL && *buf != '/' ) {
	    // buf contains a relative pathname (e.g. "../foo");
	    // resolve this to an absolute pathname.
	    if ( strlcpy(filename, buf, sizeof(filename)) >= sizeof(filename) ||
		 realpath(filename, buf) == NULL )
		buf = NULL; 
	}
    }

    return buf;
}
#elif defined(FREEBSD) || defined(NETBSD)
#include <sys/types.h>
#include <sys/sysctl.h>

char *get_exename(char *buf, size_t size)
{
  int mib[4];
  mib[0] = CTL_KERN;
#if defined(FREEBSD)
  mib[1] = KERN_PROC;
  mib[2] = KERN_PROC_PATHNAME;
  mib[3] = -1;
#else
  mib[1] = KERN_PROC_ARGS;
  mib[2] = -1;
  mib[3] = KERN_PROC_PATHNAME;
#endif
  sysctl(mib, 4, buf, &size, NULL, 0);
 
  return buf;
}
#endif