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