1 /*
2  *  This file is part of the XForms library package.
3  *
4  *  XForms is free software; you can redistribute it and/or modify it
5  *  under the terms of the GNU Lesser General Public License as
6  *  published by the Free Software Foundation; either version 2.1, or
7  *  (at your option) any later version.
8  *
9  *  XForms is distributed in the hope that it will be useful, but
10  *  WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public License
15  *  along with XForms.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 
19 /*
20    **  VMS readdir() routines.
21    **  Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
22    **  This code has no copyright.
23    **
24    **  Feb'95 -- reliance on <descrip.h> eliminated, here and in dirent.h;
25    **            support for Unix-style directory specifications implemented;
26    **            vmsreaddirversions() modified to return prior flag setting;
27    **            use ANSI headers <stdlib.h> and <string.h> plus VMS headers
28    **            <lib$routines.h> and <fscndef.h>; minor lint cleanup.   [pr]
29    **
30    **  Known problems:
31    **           garbage directory specifications aren't always diagnosed.
32  */
33 
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37 
38 #include <stdlib.h>
39 #include <string.h>
40 #include <stdio.h>
41 #include <ctype.h>
42 #include <errno.h>
43 #include <lib$routines.h>
44 #include <fscndef.h>        /* $FILESCAN arguments */
45 #include <rmsdef.h>
46 
47 
48  /* Protect against TEST being defined by some other module */
49 
50 #undef TEST
51 
52 /* Uncomment the next line to get a test routine. */
53 /* #define TEST */
54 
55  /* Number of elements in vms_versions array */
56 #define VERSIZE(e)  (sizeof e->vms_versions / sizeof e->vms_versions[0])
57 
58  /* Simplified string descriptor */
59 struct dsc
60 {
61     unsigned short len, mbz;
62     char *adr;
63 };
64 
65  /* Parts of file specification used to specify a directory. */
66 #define FSCN_DIR (FSCN$M_NODE | FSCN$M_DEVICE | FSCN$M_ROOT | FSCN$M_DIRECTORY)
67 
68 extern unsigned long sys$filescan();
69 
70  /* Filename syntax conversion routine */
71 #ifdef __DECC           /* (really depends on run-time library, not
72                    compiler) */
73 #define cvt__to_vms decc$to_vms
74 #else
75 #define cvt__to_vms shell$to_vms
76 #endif
77 extern int cvt__to_vms(char *, int (*)(char *, int), int, int);
78 static int cvt_action(char *, int);
79 
80  /* Callback routine uses non re-entrant communication w/ caller. */
81 static char nambuf[255 + 1];
82 
83 /*
84    **  XXX$to_vms callback routine.  Note: old versions of shell$to_vms
85    **  didn't document the second argument, which indicates whether a
86    **  directory of foo/bar was converted into "[.foo]bar.dir" rather
87    **  than "[.foo.bar]" (such conversion is requested by passing non-zero
88    **  as XXX$to_vms's 4th argument; also undocumented in old versions).
89  */
90 static int
cvt_action(char * name,int dir_flag___unused)91 cvt_action(char *name, int dir_flag___unused)
92 {
93     nambuf[sizeof nambuf - 1] = '\0';
94     (void) strncpy(nambuf, name, sizeof nambuf - 1);
95     /* An even value tells xxx$to_vms that conversion should stop; that point
96        is moot here since wildcard processing is disabled. */
97     return 0;
98 }
99 
100 /*
101    **  Open a directory, return a handle for later use.
102  */
103 
104 DIR *
opendir(name)105 opendir( name )
106      const char *name;
107 {
108     DIR *dd;
109     struct dsc name_dsc;
110     int len, retry = 0;
111     unsigned long flags;
112 
113     /* Validate the directory name argument.  ("[...]" is allowed!) */
114     if (!name || !*name || strchr(name, '*') != 0
115     || strchr(name, '%') != 0 || strchr(name, '?') != 0)
116     {
117     /* Name is required; wildcards are not allowed. */
118     errno = ENOTDIR;
119     return NULL;
120     }
121     nambuf[sizeof nambuf - 1] = '\0';   /* (strncpy doesn't guarantee it) */
122     name_dsc.adr = strncpy(nambuf, name, sizeof nambuf - 1);
123     name_dsc.mbz = 0;
124     do
125     {
126     name_dsc.len = len = strlen(nambuf);
127     /* Check which components of file specification seem to be present. */
128     flags = ~0;     /* (init so that we can ignore return status)
129                  */
130     (void) sys$filescan(&name_dsc, (void *) 0, &flags);
131     if ((flags & ~FSCN_DIR) != 0 || strchr(nambuf, '/') != 0)
132     {
133         /* Didn't supply nice directory name; check for Unix-style one. */
134         /* (stupid shell$to_vms doesn't like trailing slash) */
135         if (len > 1 && nambuf[len - 1] == '/')
136         nambuf[len - 1] = '\0';
137 
138         if (++retry > 1 || cvt__to_vms(nambuf, cvt_action, 0, 0) != 1)
139         {
140         errno = ENOTDIR;
141         return NULL;
142         }           /* else:        cvt_action() has updated
143                    `nambuf'; `retry' is now 1 */
144     }
145     else
146         retry = 0;      /* have something, possibly after retrying
147                    once */
148     }
149     while (retry);
150 
151     /* Get memory for the handle, and the pattern. */
152     if ((dd = fl_malloc( sizeof *dd ) ) == NULL )
153     {
154         errno = ENOMEM;
155         return NULL;
156     }
157     dd->pattern = fl_malloc( strlen( nambuf ) + sizeof "*.*" );
158     if (dd->pattern == NULL)
159     {
160         fl_free( dd );
161         errno = ENOMEM;
162         return NULL;
163     }
164 
165     /* Fill in the fields. */
166     strcat(strcpy(dd->pattern, nambuf), "*.*");
167     dd->context = 0;
168     dd->count = 0;
169     dd->vms_wantversions = 0;
170     dd->pat.adr = dd->pattern;
171     dd->pat.len = strlen(dd->pattern);
172     dd->pat.mbz = 0;
173     memset(&dd->entry, 0, sizeof dd->entry);
174 
175     return dd;
176 }
177 
178 
179 /*
180    **  Set the flag to indicate we want versions or not.  Return old setting.
181  */
182 int
vmsreaddirversions(dd,flag)183 vmsreaddirversions(dd, flag)
184      DIR *dd;
185      int flag;
186 {
187     int old_flag = dd->vms_wantversions;
188 
189     dd->vms_wantversions = flag;
190     return old_flag;
191 }
192 
193 
194 /*
195    **  Free up an opened directory.
196  */
197 
198 void
closedir(dd)199 closedir( dd )
200      DIR *dd;
201 {
202     if (dd)
203     {
204     (void) lib$find_file_end(&dd->context);
205     fl_free(dd->pattern);
206     fl_free((void *) dd);
207     }
208 }
209 
210 
211 /*
212    **  Collect all the version numbers for the current file.
213  */
214 
215 static void
collectversions(dd)216 collectversions( dd )
217      DIR *dd;
218 {
219     struct dsc pat;
220     struct dsc res;
221     struct dirent *e;
222     char *p;
223     char buff[sizeof dd->entry.d_name];
224     int i;
225     char *text;
226     long context;
227 
228     /* Convenient shorthand. */
229     e = &dd->entry;
230 
231     /* Add the name plus version wildcard, replacing the "*.*" put on before */
232     i = strlen(dd->pattern);
233     /* assert( i > 3 ); */
234     i = FL_max( i, 4 );
235     text = fl_malloc( (i - 3) + strlen(e->d_name) + sizeof ";*");
236     if (text == NULL)
237         return;
238     strcpy(text, dd->pattern);
239     strcat(strcpy(&text[i - 3], e->d_name), ";*");
240 
241     /* Set up the pattern and result descriptors. */
242     pat.adr = text;
243     pat.len = strlen(text);
244     res.adr = buff;
245     res.len = sizeof buff - 1;
246     pat.mbz = res.mbz = 0;
247 
248     /* Read files, collecting versions. */
249     for (context = 0; e->vms_verscount < VERSIZE(e); e->vms_verscount++)
250     {
251         if (lib$find_file(&pat, &res, &context) == RMS$_NMF || context == 0)
252             break;
253         buff[sizeof buff - 1] = '\0';
254         if (p = strchr(buff, ';'))
255             e->vms_versions[e->vms_verscount] = atoi(p + 1);
256         else
257             e->vms_versions[e->vms_verscount] = -1;
258     }
259     if (e->vms_verscount < VERSIZE(e))
260         e->vms_versions[e->vms_verscount] = -1;
261 
262     (void) lib$find_file_end(&context);
263     fl_free(text);
264 }
265 
266 
267 /*
268    **  Read the next entry from the directory.
269  */
270 struct dirent *
readdir(dd)271 readdir(dd)
272      DIR *dd;
273 {
274     struct dsc res;
275     char *p, *q;
276     int i;
277 
278     /* Set up result descriptor, and get next file. */
279     res.adr = nambuf;
280     res.len = sizeof nambuf - 1;
281     res.mbz = 0;
282     dd->count++;
283     /* (ought to check for generic success/failure, not a specific value) */
284     if (lib$find_file(&dd->pat, &res, &dd->context) == RMS$_NMF
285     || dd->context == 0L)
286     /* None left... */
287     return NULL;
288 
289     /* Strip trailing blanks. */
290     nambuf[sizeof nambuf - 1] = '\0';
291     for (p = &nambuf[sizeof nambuf - 1]; p > nambuf && *--p == ' ';)
292     *p = '\0';
293 
294     /* Skip any directory component and just copy the name. */
295     p = nambuf;
296     while ((q = strchr(p, ']')) != 0 || (q = strchr(p, '>')) != 0)
297     p = q + 1;
298     (void) strncpy(dd->entry.d_name, p, sizeof dd->entry.d_name - 1);
299     dd->entry.d_name[sizeof dd->entry.d_name - 1] = '\0';
300 
301     /* Clobber the version. */
302     if ((p = strchr(dd->entry.d_name, ';')) != 0)
303     *p = '\0';
304 
305     dd->entry.vms_verscount = 0;
306     dd->entry.vms_versions[0] = 0;
307     if (dd->vms_wantversions)
308     collectversions(dd);
309     return &dd->entry;
310 }
311 
312 
313 /*
314    **  Return something that can be used in a seekdir later.
315  */
316 long
telldir(dd)317 telldir(dd)
318      DIR *dd;
319 {
320     return dd->count;
321 }
322 
323 
324 /*
325    **  Return to a spot where we used to be.  Brute force.
326  */
327 void
seekdir(dd,count)328 seekdir(dd, count)
329      DIR *dd;
330      long count;
331 {
332     int vms_wantversions;
333 
334     /* If we haven't done anything yet... */
335     if (dd->count == 0)
336     return;
337 
338     /* Remember some state, and clear it. */
339     vms_wantversions = dd->vms_wantversions;
340     dd->vms_wantversions = 0;
341     (void) lib$find_file_end(&dd->context);
342     dd->context = 0;
343 
344     /* The increment is in readdir(). */
345     for (dd->count = 0; dd->count < count;)
346     (void) readdir(dd);
347 
348     dd->vms_wantversions = vms_wantversions;
349 }
350 
351 
352 /*
353  * Local variables:
354  * tab-width: 4
355  * indent-tabs-mode: nil
356  * End:
357  */
358