1 #ifdef OS2
2 
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <ctype.h>
7 
8 #include <dirent.h>
9 #include <errno.h>
10 
11 /*#ifndef __EMX__
12 #include <libx.h>
13 #endif */
14 
15 #define INCL_DOSFILEMGR
16 #define INCL_DOSERRORS
17 #include <os2.h>
18 
19 #if OS2 >= 2
20 #define FFBUF FILEFINDBUF3
21 #define Word ULONG
22 /*
23    * LS20 recommends a request count of 100, but according to the
24    * APAR text it does not lead to missing files, just to funny
25    * numbers of returned entries.
26    *
27    * LS30 HPFS386 requires a count greater than 2, or some files
28    * are missing (those starting with a character less that '.').
29    *
30    * Novell loses entries which overflow the buffer. In previous
31    * versions of dirent2, this could have lead to missing files
32    * when the average length of 100 directory entries was 40 bytes
33    * or more (quite unlikely for files on a Novell server).
34    *
35    * Conclusion: Make sure that the entries all fit into the buffer
36    * and that the buffer is large enough for more than 2 entries
37    * (each entry is at most 300 bytes long). And ignore the LS20
38    * effect.
39    */
40 #define Count 25
41 #define BufSz (25 * (sizeof(FILEFINDBUF3) + 1))
42 #else
43 #define FFBUF FILEFINDBUF
44 #define Word USHORT
45 #define BufSz 1024
46 #define Count 3
47 #endif
48 
49 #if defined(__IBMC__) || defined(__IBMCPP__)
50 #define error(rc) _doserrno = rc, errno = EOS2ERR
51 #elif defined(MICROSOFT)
52 #define error(rc) _doserrno = rc, errno = 255
53 #else
54 #define error(rc) errno = 255
55 #endif
56 
57 struct _dirdescr {
58     HDIR handle;               /* DosFindFirst handle */
59     char fstype;               /* filesystem type */
60     Word count;                /* valid entries in <ffbuf> */
61     long number;               /* absolute number of next entry */
62     int index;                 /* relative number of next entry */
63     FFBUF *next;               /* pointer to next entry */
64     char name[MAXPATHLEN + 3]; /* directory name */
65     unsigned attrmask;         /* attribute mask for seekdir */
66     struct dirent entry;       /* buffer for directory entry */
67     BYTE ffbuf[BufSz];
68 };
69 
70 /*
71  * Return first char of filesystem type, or 0 if unknown.
72  */
73 static char
getFSType(const char * path)74 getFSType(const char *path)
75 {
76     static char cache[1 + 26];
77     char drive[3], info[512];
78     Word unit, infolen;
79     char r;
80 
81     if (isalpha(path[0]) && path[1] == ':') {
82         unit = toupper(path[0]) - '@';
83         path += 2;
84     } else {
85         ULONG driveMap;
86 #if OS2 >= 2
87         if (DosQueryCurrentDisk(&unit, &driveMap))
88 #else
89         if (DosQCurDisk(&unit, &driveMap))
90 #endif
91             return 0;
92     }
93 
94     if ((path[0] == '\\' || path[0] == '/') &&
95         (path[1] == '\\' || path[1] == '/'))
96         return 0;
97 
98     if (cache[unit])
99         return cache[unit];
100 
101     drive[0] = '@' + unit;
102     drive[1] = ':';
103     drive[2] = '\0';
104     infolen = sizeof info;
105 #if OS2 >= 2
106     if (DosQueryFSAttach(drive, 0, FSAIL_QUERYNAME, (PVOID)info, &infolen))
107         return 0;
108     if (infolen >= sizeof(FSQBUFFER2)) {
109         FSQBUFFER2 *p = (FSQBUFFER2 *)info;
110         r = p->szFSDName[p->cbName];
111     } else
112 #else
113     if (DosQFSAttach((PSZ)drive, 0, FSAIL_QUERYNAME, (PVOID)info, &infolen, 0))
114         return 0;
115     if (infolen >= 9) {
116         char *p = info + sizeof(USHORT);
117         p += sizeof(USHORT) + *(USHORT *)p + 1 + sizeof(USHORT);
118         r = *p;
119     } else
120 #endif
121         r = 0;
122     return cache[unit] = r;
123 }
124 
125 char *
abs_path(const char * name,char * buffer,int len)126 abs_path(const char *name, char *buffer, int len)
127 {
128     char buf[4];
129     if (isalpha(name[0]) && name[1] == ':' && name[2] == '\0') {
130         buf[0] = name[0];
131         buf[1] = name[1];
132         buf[2] = '.';
133         buf[3] = '\0';
134         name = buf;
135     }
136 #if OS2 >= 2
137     if (DosQueryPathInfo((PSZ)name, FIL_QUERYFULLNAME, buffer, len))
138 #else
139     if (DosQPathInfo((PSZ)name, FIL_QUERYFULLNAME, (PBYTE)buffer, len, 0L))
140 #endif
141         return NULL;
142     return buffer;
143 }
144 
145 DIR *
openxdir(const char * path,unsigned att_mask)146 openxdir(const char *path, unsigned att_mask)
147 {
148     DIR *dir;
149     char name[MAXPATHLEN + 3];
150     Word rc;
151 
152     dir = malloc(sizeof(DIR));
153     if (dir == NULL) {
154         errno = ENOMEM;
155         return NULL;
156     }
157 
158     strncpy(name, path, MAXPATHLEN);
159     name[MAXPATHLEN] = '\0';
160     switch (name[strlen(name) - 1]) {
161         default:
162             strcat(name, "\\");
163         case '\\':
164         case '/':
165         case ':':;
166     }
167     strcat(name, ".");
168     if (!abs_path(name, dir->name, MAXPATHLEN + 1))
169         strcpy(dir->name, name);
170     if (dir->name[strlen(dir->name) - 1] == '\\')
171         strcat(dir->name, "*");
172     else
173         strcat(dir->name, "\\*");
174 
175     dir->fstype = getFSType(dir->name);
176     dir->attrmask = att_mask | A_DIR;
177 
178     dir->handle = HDIR_CREATE;
179     dir->count = 100;
180 #if OS2 >= 2
181     rc = DosFindFirst(dir->name, &dir->handle, dir->attrmask,
182                       dir->ffbuf, sizeof dir->ffbuf, &dir->count, FIL_STANDARD);
183 #else
184     rc = DosFindFirst((PSZ)dir->name, &dir->handle, dir->attrmask,
185                       (PFILEFINDBUF)dir->ffbuf, sizeof dir->ffbuf, &dir->count, 0);
186 #endif
187     switch (rc) {
188         default:
189             free(dir);
190             error(rc);
191             return NULL;
192         case NO_ERROR:
193         case ERROR_NO_MORE_FILES:;
194     }
195 
196     dir->number = 0;
197     dir->index = 0;
198     dir->next = (FFBUF *)dir->ffbuf;
199 
200     return (DIR *)dir;
201 }
202 
203 DIR *
opendir(const char * pathname)204 opendir(const char *pathname)
205 {
206     return openxdir(pathname, 0);
207 }
208 
209 struct dirent *
readdir(DIR * dir)210 readdir(DIR *dir)
211 {
212     static int dummy_ino = 2;
213 
214     if (dir->index == dir->count) {
215         Word rc;
216         dir->count = 100;
217 #if OS2 >= 2
218         rc = DosFindNext(dir->handle, dir->ffbuf,
219                          sizeof dir->ffbuf, &dir->count);
220 #else
221         rc = DosFindNext(dir->handle, (PFILEFINDBUF)dir->ffbuf,
222                          sizeof dir->ffbuf, &dir->count);
223 #endif
224         if (rc) {
225             error(rc);
226             return NULL;
227         }
228 
229         dir->index = 0;
230         dir->next = (FFBUF *)dir->ffbuf;
231     }
232 
233     if (dir->index == dir->count)
234         return NULL;
235 
236     memcpy(dir->entry.d_name, dir->next->achName, dir->next->cchName);
237     dir->entry.d_name[dir->next->cchName] = '\0';
238     dir->entry.d_ino = dummy_ino++;
239     dir->entry.d_reclen = dir->next->cchName;
240     dir->entry.d_namlen = dir->next->cchName;
241     dir->entry.d_size = dir->next->cbFile;
242     dir->entry.d_attribute = dir->next->attrFile;
243     dir->entry.d_time = *(USHORT *)&dir->next->ftimeLastWrite;
244     dir->entry.d_date = *(USHORT *)&dir->next->fdateLastWrite;
245 
246     switch (dir->fstype) {
247         case 'F': /* FAT */
248         case 'C': /* CDFS */
249             if (dir->next->attrFile & FILE_DIRECTORY)
250                 strupr(dir->entry.d_name);
251             else
252                 strlwr(dir->entry.d_name);
253     }
254 
255 #if OS2 >= 2
256     dir->next = (FFBUF *)((BYTE *)dir->next + dir->next->oNextEntryOffset);
257 #else
258     dir->next = (FFBUF *)((BYTE *)dir->next->achName + dir->next->cchName + 1);
259 #endif
260     ++dir->number;
261     ++dir->index;
262 
263     return &dir->entry;
264 }
265 
266 long
telldir(DIR * dir)267 telldir(DIR *dir)
268 {
269     return dir->number;
270 }
271 
272 void
seekdir(DIR * dir,long off)273 seekdir(DIR *dir, long off)
274 {
275     if (dir->number > off) {
276         char name[MAXPATHLEN + 2];
277         Word rc;
278 
279         DosFindClose(dir->handle);
280 
281         strcpy(name, dir->name);
282         strcat(name, "*");
283 
284         dir->handle = HDIR_CREATE;
285         dir->count = 32767;
286 #if OS2 >= 2
287         rc = DosFindFirst(name, &dir->handle, dir->attrmask,
288                           dir->ffbuf, sizeof dir->ffbuf, &dir->count, FIL_STANDARD);
289 #else
290         rc = DosFindFirst((PSZ)name, &dir->handle, dir->attrmask,
291                           (PFILEFINDBUF)dir->ffbuf, sizeof dir->ffbuf, &dir->count, 0);
292 #endif
293         switch (rc) {
294             default:
295                 error(rc);
296                 return;
297             case NO_ERROR:
298             case ERROR_NO_MORE_FILES:;
299         }
300 
301         dir->number = 0;
302         dir->index = 0;
303         dir->next = (FFBUF *)dir->ffbuf;
304     }
305 
306     while (dir->number < off && readdir(dir))
307         ;
308 }
309 
310 void
closedir(DIR * dir)311 closedir(DIR *dir)
312 {
313     DosFindClose(dir->handle);
314     free(dir);
315 }
316 
317 /*****************************************************************************/
318 
319 #ifdef TEST
320 
main(int argc,char ** argv)321 main(int argc, char **argv)
322 {
323     int i;
324     DIR *dir;
325     struct dirent *ep;
326 
327     for (i = 1; i < argc; ++i) {
328         dir = opendir(argv[i]);
329         if (!dir)
330             continue;
331         while (ep = readdir(dir))
332             if (strchr("\\/:", argv[i][strlen(argv[i]) - 1]))
333                 printf("%s%s\n", argv[i], ep->d_name);
334             else
335                 printf("%s/%s\n", argv[i], ep->d_name);
336         closedir(dir);
337     }
338 
339     return 0;
340 }
341 
342 #endif
343 
344 #endif /* OS2 */
345