summaryrefslogtreecommitdiff
path: root/dmake/msdos/dirlib.c
blob: eaef928772afba7e172917e95f82d3ae6efb57ab (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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
/*
            DIRLIB for MS-DOS
            -----------------

Enclosed is an implementation of the `dirlib' package for MS-DOS.
The implementation is targeted for MS-C, although any reasonably
competent C compiler should manage.  The package consists of:

    dir.h       the header file
    dir.c       the functions
    testdir.c   a q&d test program

The package tries to view directory naming in a Un*x light; in particular,
directories such as '/.' and '/..' (as well as `.' and `..' if your
current directory is root) are understood.   Indefinite paths like
`/../.././../..' will correctly refer to the root (of the particular disk).
Names such as `a:////./../' are okay too.

I've tried to be as sensible about DTA's as possible, since you never
know who will be using one; they are set before use, and reset afterwards.

There is some cruft in the package, namely the way `seekdir' and
`telldir' are done.  The code was derived from a little experimentation,
and may not work after a certain point (although I believe the 2.x version
to be solid).  Caveat utilitor.

Documentation for the package is available in the public domain; the
package's functionality was derived from this documentation.

Bug reports and comments are welcome.  Enjoy!

                - Matt

-------
UUCP:   {ucbvax,ihnp4,randvax,trwrb!trwspp,ism780}!ucla-cs!matt
ARPA:   matt@LOCUS.UCLA.EDU
Ph: (213) 825-2756

--------
Modified for use in dmake by Dennis Vadura.  Mostly just clean up and an
effort to make correctly typed objects are passed to functions in find.c.
Also deleted all dos version 2.0 specific code.  It is not required any
more.
*/

/*
 * revision history:
 *
 *  VER MM/DD/YY    COMMENTS
 *  ----    --------    --------
 *  0.99    02/24/86    Beta release to INTERNET
 */

#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <dos.h>

#include "dirent.h"

extern int find_err;


static char *
getdcwd(drive)
int drive;
{
   union REGS r;
   struct SREGS s;
   static char xcwd[64];
   char far *cwd = xcwd;

   r.h.ah = 0x47;
   r.h.dl = drive;
   r.x.si = FP_OFF(cwd);
   s.ds = FP_SEG(cwd);
   intdosx(&r, &r, &s);
   find_err = r.x.ax;
   if (r.x.cflag)
      return (char *) 0;
   return xcwd;
}



/*
 * opendir
 */

#define SUFFIX  "\\*.*"
#define SLASH   "\\"
#define streq(a,b)  (strcmp(a,b)==0)

DIR *
opendir(name)
char *name;
{
   register DIR *nd;
   char *cwd;
   char drive[3];
   int atroot = 0;
   int rooted = 0;

   /*
   * hack off drive designator if present
   */

   if (name[1] == ':') {
      cwd = getdcwd(toupper(name[0]) - 'A' + 1);
      drive[0] = name[0]; drive[1] = ':'; drive[2] = '\0';
      name += 2;
   }
   else {
      cwd = getdcwd(0);
      drive[0] = '\0';
   }

   /* is the name 'rooted'? */
   if ((*name == '/') || (*name == '\\')) ++rooted;

   /* see if we are at the root directory for this device */
   if (!*cwd) ++atroot;

   /*
   * MSDOS '/' doesn't have a '.' or '..'
   * also, double '/' sequences don't make sense.
   * many ported programs expect them to work, so we fix it up...
   */

   /* chop off leading . and .. if at root */
   if (atroot && (*name == '.')) {
      switch (*++name) {
     case '\0':
     case '/':
     case '\\':
        break;

     case '.':
        switch (*++name) {
           case '\0':
           case '/':
           case '\\':
            break;
           default:
            --name;
            --name;
        }
        break;

     default:
        --name;
      }
   }

   /* chop off leading /'s, /.'s and /..'s to make naming sensible */
   while (*name && ((*name == '/') || (*name == '\\'))) {
      if (*++name == '.') {
     switch (*++name) {
        case '\0':
        case '/':
        case '\\':
           break;

        case '.':
           switch (*++name) {
          case '\0':
          case '/':
          case '\\':
              break;

         default:
              --name;
              --name;
           }
           break;

        default:
           --name;
     }
      }
   }


   /*
   * name should now look like: path/path/path
   * we must now construct name based on whether or not it
   * was 'rooted' (started with a /)
   */

   if (rooted) cwd = "";

   /* construct DIR */
   if (!(nd = (DIR *)malloc(
      sizeof(DIR)+strlen(drive)+strlen(cwd)+strlen(SLASH)+
      strlen(name)+strlen(SUFFIX))))
      return (DIR *) 0;

   /* create long name */
   strcpy(nd->dd_name, drive);
   if (*cwd) {
      strcat(nd->dd_name, SLASH);
      strcat(nd->dd_name, cwd);
   }
   if (*name) {
      strcat(nd->dd_name, SLASH);
      strcat(nd->dd_name, name);
   }
   strcat(nd->dd_name, SUFFIX);

   /* search */
   if (!findfirst(&nd->dd_name[0], &nd->dd_dta)) {
      free((char *)nd);
      errno = ENOENT;
      return (DIR *) 0;
   }
   nd->dd_stat = 0;
   return nd;
}


struct dirent *
readdir(dirp)
DIR *dirp;
{
   static struct dirent dir;

   if (dirp->dd_stat)
      return (struct dirent *) 0;

   /* format structure */
   dir.d_ino = 0; /* not valid for DOS */
   dir.d_reclen = 0;
   strcpy(dir.d_name, dirp->dd_dta.name);
   dir.d_namlen = strlen(dir.d_name);
   strlwr(dir.d_name); /* DOSism */

   /* read ahead */
   if (findnext(&dirp->dd_dta) != NULL)
      dirp->dd_stat = 0;
   else
      dirp->dd_stat = find_err;

   return &dir;
}


void
closedir(dirp)
DIR *dirp;
{
   free((char *)dirp);
}


void
seekdir(dirp, pos)
DIR *dirp;
long pos;
{
   /*
   * check against DOS limits
   */

   if ((pos < 0) || (pos > 4095)) {
      dirp->dd_stat = 1;
      return;
   }

   *(short *)&dirp->dd_dta.fcb[13] = pos + 1;

   /* read ahead */
   if (findnext(&dirp->dd_dta))
      dirp->dd_stat = 0;
   else
      dirp->dd_stat = find_err;
}


long
telldir(dirp)
DIR *dirp;
{
   return (long) (*(short *)&dirp->dd_dta.fcb[13] - 2);
}