a getwd that works

pefv700 at perv.pe.utexas.edu pefv700 at perv.pe.utexas.edu
Sun Jan 13 14:42:45 AEST 1991



As was pointed out, I was wrong about getwd(3)'s return value.  Sorry.
Nevertheless, it can fail for pathnames that are too long and I need something
better.

So I wrote my own.  Here's the code with a main() included, effectively making
it pwd(1).  Interestingly enough, for really long pathnames this version will
work ok while the pwd(1) on my machine (a Sun) will cut off part of the 
beginning of the pathname.  One caveat: On this Sun, cc -O3 works well.  I did
try it on a Convex and it seemed slow, even with optimization.

All flames and improvements to the code are encouraged.

/* pwd */
/* Christopher Phillips */
/* 1/11/90 */

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <sys/param.h>
#include <malloc.h>

main()
{
    char *cwd, *getwd();

    if ((cwd = getwd()) == NULL) {
	perror("pwd");
	return 1;
    } else {
	printf("%s\n", cwd);
	return 0;
    }
}

/* From here down (plus the above #include's) is getwd(3) except for 
   3 differences:
   1) the calling syntax is now   char *getwd()
   2) the absolute pathname may be longer than MAXPATHLEN 
   3) since malloc(3V) (and subsequently realloc(3V)) 
      is used, the result may be freed using free(3V) */

char **beginptr = NULL, *endptr = NULL;
int length = 0, maxlen = 1;
int rootinode, rootdev;

char *getwd()
{
    char *dir, *buildwd();
    struct stat rootbuf;

    if ((dir = malloc(2)) == NULL) {
	perror("getwd: can't malloc");
	return NULL;
    }
    beginptr = &dir;
    endptr = dir;

    if (stat("/", &rootbuf) == -1) {
	perror("getwd: can't stat /");
	return NULL;
    }
    rootinode = rootbuf.st_ino;
    rootdev = rootbuf.st_dev;

    return buildwd();
}

/* buildwd recursively "builds" the directory name backwards until the
   root directory is reached, in which case the name is reversed */
static char *buildwd()
{
    ino_t inode;
    dev_t dev;
    int add();
    struct stat stbuf;
    DIR *dfd;
    struct dirent *dp;
    void reverse();

    if (stat(".", &stbuf) == -1) {
	perror("getwd: can't stat .");
	return NULL;
    }
    inode = stbuf.st_ino;
    dev = stbuf.st_dev;
    if (inode == rootinode && dev == rootdev) {
	if (**beginptr == '\0')
	    return "/";
	else {
	    reverse(*beginptr);
	    return *beginptr;
	}
    } else if (chdir("..") == -1) {
	perror("getwd: can't chdir ..");
	return NULL;
    }
    if ((dfd = opendir(".")) == NULL) {
	perror("getwd: can't open dir .");
	return NULL;
    }
    while ((dp = readdir(dfd)) != NULL) {
	if (stat(dp->d_name, &stbuf) == -1) {
	    perror("getwd: can't stat .");
	    return NULL;
	}
	if (stbuf.st_ino == inode && stbuf.st_dev == dev)
	    break;
    }
    if (dp == NULL)
	return NULL;
    if (add(beginptr, &endptr, dp->d_name, dp->d_namlen) == -1)
	return NULL;
    if (buildwd() == NULL)
	return NULL;
    else
	return *beginptr;
}

/* add reverse "str" and adds it and a "/" to the directory name */
int add(first, last, str, len)
char **first, **last, *str;
int len;
{
    off_t diff;

    if (length + len + 2 > maxlen) {   /* 1 for '/' and 1 for '\0' */
	maxlen += MAXPATHLEN;
	diff = *last - *first;
	if ((*first = realloc(*first, maxlen + 1)) == NULL) {
	    perror("getwd: can't realloc");
	    return -1;
	}
	*last = *first + diff;
    }
    reverse(str);
    *last = strcpy(*last, str) + len;
    *(*last)++ = '/'; /* realloc will make sure the string is null-terminated */
    return 0;
}

/* reverse reverse a string so that "string" becomes "gnirts" */
void reverse(s)
char *s;
{
    int len, i;
    char *t, c;

    len = strlen(s);
    t = &s[len - 1];
    for (i = 0; i < len / 2; i++) {
	c = *s;
	*s++ = *t;
	*t-- = c;
    }
}



More information about the Comp.unix.programmer mailing list