summaryrefslogtreecommitdiff
path: root/dmake/unix/sysvr3/pwd/getcwd.c
blob: f2359bcc795bbe23d8aa887f8ee5e2a715a63529 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/*
    getcwd -- get pathname of current working directory

    public-domain implementation

    last edit:  03-Nov-1990 Gwyn@BRL.MIL

    complies with the following standards:
        IEEE Std 1003.1-1988
        SVID Issue 3
        X/Open Portability Guide Issue 2 (when "XPG2" is defined)
        X/Open Portability Guide Issue 3

    This implementation of getcwd() can be used to replace the UNIX
    System V library routine (which uses popen() to capture the output of
    the "pwd" command).  Once that is done, "pwd" can be reimplemented as
    just puts(getcwd((char*)0,0)), assuming "XPG2" is defined below.

    This implementation depends on every directory having entries for
    "." and "..".  It also depends on the internals of the <dirent.h>
    data structures to some degree.

    I considered using chdir() to ascend the hierarchy, followed by a
    final chdir() to the path being returned by getcwd() to restore the
    location, but decided that error recovery was too difficult that way.
    The algorithm I settled on was inspired by my rewrite of the "pwd"
    utility, combined with the dotdots[] array trick from the SVR2 shell.
*/
#define XPG2    /* define to support obsolete XPG2-mandated feature */


#include    <sys/types.h>
#include    <sys/stat.h>

#ifdef M_XENIX
# include        <sys/ndir.h>
# define dirent direct
#else
# include        <dirent.h>
#endif

#include    <errno.h>
#include    <string.h>

typedef char    *pointer;       /* (void *) if you have it */

extern void free();
extern pointer  malloc();
extern int  fstat(), stat();

extern int  errno;          /* normally done by <errno.h> */

#ifndef NULL
#define NULL    0           /* amorphous null pointer constant */
#endif

#ifndef NAME_MAX
#define NAME_MAX    255     /* maximum directory entry size */
#endif


char    *
getcwd( buf, size )         /* returns pointer to CWD pathname */
    char        *buf;       /* where to put name (NULL to malloc) */
    int     size;       /* size of buf[] or malloc()ed memory */
    {
    static char dotdots[] =
"../../../../../../../../../../../../../../../../../../../../../../../../../..";
    char        *dotdot;    /* -> dotdots[.], right to left */
    DIR     *dirp;      /* -> parent directory stream */
    struct dirent   *dir;       /* -> directory entry */
    struct stat stat1,
            stat2;      /* info from stat() */
    struct stat *d = &stat1;    /* -> info about "." */
    struct stat *dd = &stat2;   /* -> info about ".." */
    register char   *buffer;    /* local copy of buf, or malloc()ed */
    char        *bufend;    /* -> buffer[size] */
    register char   *endp;      /* -> end of reversed string */
    register char   *dname;     /* entry name ("" for root) */
    int     serrno = errno; /* save entry errno */

    if ( buf != NULL && size <= 0
#ifndef XPG2
      || buf == NULL
#endif
       )    {
        errno = EINVAL;     /* invalid argument */
        return NULL;
        }

    buffer = buf;
#ifdef XPG2
    if ( buf == NULL        /* wants us to malloc() the string */
      && (buffer = (char *) malloc( (unsigned) size )) == NULL
    /* XXX -- actually should probably not pay attention to "size" arg */
       )    {
        errno = ENOMEM;     /* cannot malloc() specified size */
        return NULL;
        }
#endif

    if ( stat( ".", dd ) != 0 ) /* prime the pump */
        goto error;     /* errno already set */

    endp = buffer;          /* initially, empty string */
    bufend = &buffer[size];

    for ( dotdot = &dotdots[sizeof dotdots]; dotdot != dotdots; )
        {
        dotdot -= 3;        /* include one more "/.." section */
                    /* (first time is actually "..") */

        /* swap stat() info buffers */
        {
        register struct stat    *temp = d;

        d = dd;         /* new current dir is old parent dir */
        dd = temp;
        }

        if ( (dirp = opendir( dotdot )) == NULL )   /* new parent */
            goto error; /* errno already set */

        if ( fstat( dirp->dd_fd, dd ) != 0 )
            {
            serrno = errno; /* set by fstat() */
            (void)closedir( dirp );
            errno = serrno; /* in case closedir() clobbered it */
            goto error;
            }

        if ( d->st_dev == dd->st_dev )
            {       /* not crossing a mount point */
            if ( d->st_ino == dd->st_ino )
                {   /* root directory */
                dname = "";
                goto append;
                }

            do
                if ( (dir = readdir( dirp )) == NULL )
                    {
                    (void)closedir( dirp );
                    errno = ENOENT; /* missing entry */
                    goto error;
                    }
            while ( dir->d_ino != d->st_ino );
            }
        else    {       /* crossing a mount point */
            struct stat t;  /* info re. test entry */
            char        name[sizeof dotdots + 1 + NAME_MAX];

            (void)strcpy( name, dotdot );
            dname = &name[strlen( name )];
            *dname++ = '/';

            do  {
                if ( (dir = readdir( dirp )) == NULL )
                    {
                    (void)closedir( dirp );
                    errno = ENOENT; /* missing entry */
                    goto error;
                    }

                (void)strcpy( dname, dir->d_name );
                /* must fit if NAME_MAX is not a lie */
                }
            while ( stat( name, &t ) != 0
                 || t.st_ino != d->st_ino
                 || t.st_dev != d->st_dev
                  );
            }

        dname = dir->d_name;

        /* append "/" and reversed dname string onto buffer */
    append:
        if ( endp != buffer /* avoid trailing / in final name */
          || dname[0] == '\0'   /* but allow "/" when CWD is root */
           )
            *endp++ = '/';

        {
        register char   *app;   /* traverses dname string */

        for ( app = dname; *app != '\0'; ++app )
            ;

        if ( app - dname >= bufend - endp )
            {
            (void)closedir( dirp );
            errno = ERANGE; /* won't fit allotted space */
            goto error;
            }

        while ( app != dname )
            *endp++ = *--app;
        }

        (void)closedir( dirp );

        if ( dname[0] == '\0' ) /* reached root; wrap it up */
            {
            register char   *startp;    /* -> buffer[.] */

            *endp = '\0';   /* plant null terminator */

            /* straighten out reversed pathname string */
            for ( startp = buffer; --endp > startp; ++startp )
                {
                char    temp = *endp;

                *endp = *startp;
                *startp = temp;
                }

            errno = serrno; /* restore entry errno */
            /* XXX -- if buf==NULL, realloc here? */
            return buffer;
            }
        }

    errno = ENOMEM;         /* actually, algorithm failure */

    error:
    if ( buf == NULL )
        free( (pointer)buffer );

    return NULL;
    }