1 | /* vms_misc.c -- sustitute code for missing/different run-time library routines.
|
---|
2 |
|
---|
3 | Copyright (C) 1991-1993, 1996-1997, 2001, 2003 the Free Software Foundation, Inc.
|
---|
4 |
|
---|
5 | This program is free software; you can redistribute it and/or modify
|
---|
6 | it under the terms of the GNU General Public License as published by
|
---|
7 | the Free Software Foundation; either version 2, or (at your option)
|
---|
8 | any later version.
|
---|
9 |
|
---|
10 | This program is distributed in the hope that it will be useful,
|
---|
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
13 | GNU General Public License for more details.
|
---|
14 |
|
---|
15 | You should have received a copy of the GNU General Public License
|
---|
16 | along with this program; if not, write to the Free Software Foundation,
|
---|
17 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
---|
18 |
|
---|
19 | #define creat creat_dummy /* one of gcc-vms's headers has bad prototype */
|
---|
20 | #include "awk.h"
|
---|
21 | #include "vms.h"
|
---|
22 | #undef creat
|
---|
23 | #include <fab.h>
|
---|
24 | #ifndef O_RDONLY
|
---|
25 | #include <fcntl.h>
|
---|
26 | #endif
|
---|
27 | #include <rmsdef.h>
|
---|
28 | #include <ssdef.h>
|
---|
29 | #include <stsdef.h>
|
---|
30 |
|
---|
31 | /*
|
---|
32 | * VMS uses a completely different status scheme (odd => success,
|
---|
33 | * even => failure), so we'll trap calls to exit() and alter the
|
---|
34 | * exit status code. [VAXC can't handle this as a macro.]
|
---|
35 | */
|
---|
36 | #ifdef exit
|
---|
37 | # undef exit
|
---|
38 | #endif
|
---|
39 | void
|
---|
40 | vms_exit( int final_status )
|
---|
41 | {
|
---|
42 | exit(final_status == 0 ? SS$_NORMAL : (SS$_ABORT | STS$M_INHIB_MSG));
|
---|
43 | }
|
---|
44 | #define exit(v) vms_exit(v)
|
---|
45 |
|
---|
46 | /*
|
---|
47 | * In VMS's VAXCRTL, strerror() takes an optional second argument.
|
---|
48 | * #define strerror(errnum) strerror(errnum,vaxc$errno)
|
---|
49 | * is all that's needed, but VAXC can't handle that (gcc can).
|
---|
50 | * [The 2nd arg is used iff errnum == EVMSERR.]
|
---|
51 | */
|
---|
52 | #ifdef strerror
|
---|
53 | # undef strerror
|
---|
54 | #endif
|
---|
55 | extern char *strerror P((int,...));
|
---|
56 |
|
---|
57 | /* vms_strerror() -- convert numeric error code into text string */
|
---|
58 | char *
|
---|
59 | vms_strerror( int errnum )
|
---|
60 | {
|
---|
61 | return ( errnum != EVMSERR ? strerror(errnum)
|
---|
62 | : strerror(EVMSERR, vaxc$errno) );
|
---|
63 | }
|
---|
64 | # define strerror(v) vms_strerror(v)
|
---|
65 |
|
---|
66 | /*
|
---|
67 | * Miscellaneous utility routine, not part of the run-time library.
|
---|
68 | */
|
---|
69 | /* vms_strdup() - allocate some new memory and copy a string into it */
|
---|
70 | char *
|
---|
71 | vms_strdup( const char *str )
|
---|
72 | {
|
---|
73 | char *result;
|
---|
74 | int len = strlen(str);
|
---|
75 |
|
---|
76 | emalloc(result, char *, len+1, "strdup");
|
---|
77 | return strcpy(result, str);
|
---|
78 | }
|
---|
79 |
|
---|
80 | /*
|
---|
81 | * VAXCRTL does not contain unlink(). This replacement has limited
|
---|
82 | * functionality which is sufficient for GAWK's needs. It works as
|
---|
83 | * desired even when we have the file open.
|
---|
84 | */
|
---|
85 | /* unlink(file) -- delete a file (ignore soft links) */
|
---|
86 | int
|
---|
87 | unlink( const char *file_spec ) {
|
---|
88 | char tmp[255+1]; /*(should use alloca(len+2+1)) */
|
---|
89 | extern int delete(const char *);
|
---|
90 |
|
---|
91 | strcpy(tmp, file_spec); /* copy file name */
|
---|
92 | if (strchr(tmp, ';') == NULL)
|
---|
93 | strcat(tmp, ";0"); /* append version number */
|
---|
94 | return delete(tmp);
|
---|
95 | }
|
---|
96 |
|
---|
97 | /*
|
---|
98 | * Work-around an open(O_CREAT+O_TRUNC) bug (screwed up modification
|
---|
99 | * and creation dates when new version is created), and also use some
|
---|
100 | * VMS-specific file options. Note: optional 'prot' arg is completely
|
---|
101 | * ignored; gawk doesn't need it.
|
---|
102 | */
|
---|
103 | #ifdef open
|
---|
104 | # undef open
|
---|
105 | #endif
|
---|
106 | extern int creat P((const char *,int,...));
|
---|
107 | extern int open P((const char *,int,unsigned,...));
|
---|
108 |
|
---|
109 | /* vms_open() - open a file, possibly creating it */
|
---|
110 | int
|
---|
111 | vms_open( const char *name, int mode, ... )
|
---|
112 | {
|
---|
113 | int result;
|
---|
114 |
|
---|
115 | if (STREQN(name, "/dev/", 5)) {
|
---|
116 | /* (this used to be handled in vms_devopen(), but that is only
|
---|
117 | called when opening files for output; we want it for input too) */
|
---|
118 | if (strcmp(name + 5, "null") == 0) /* /dev/null -> NL: */
|
---|
119 | name = "NL:";
|
---|
120 | else if (strcmp(name + 5, "tty") == 0) /* /dev/tty -> TT: */
|
---|
121 | name = "TT:";
|
---|
122 | }
|
---|
123 |
|
---|
124 | if (mode == (O_WRONLY|O_CREAT|O_TRUNC)) {
|
---|
125 | /* explicitly force stream_lf record format to override DECC$SHR's
|
---|
126 | defaulting of RFM to earlier file version's when one is present */
|
---|
127 | result = creat(name, 0, "rfm=stmlf", "shr=nil", "mbc=32");
|
---|
128 | } else {
|
---|
129 | struct stat stb;
|
---|
130 | const char *mbc, *shr = "shr=get", *ctx = "ctx=stm";
|
---|
131 |
|
---|
132 | if (stat((char *)name, &stb) < 0) { /* assume DECnet */
|
---|
133 | mbc = "mbc=8";
|
---|
134 | } else { /* ordinary file; allow full sharing iff record format */
|
---|
135 | mbc = "mbc=32";
|
---|
136 | if ((stb.st_fab_rfm & 0x0F) < FAB$C_STM) shr = "shr=get,put,upd";
|
---|
137 | }
|
---|
138 | result = open(name, mode, 0, shr, mbc, "mbf=2");
|
---|
139 | }
|
---|
140 |
|
---|
141 | /* This is only approximate; the ACP -> RMS -> VAXCRTL interface
|
---|
142 | discards too much potentially useful status information... */
|
---|
143 | if (result < 0 && errno == EVMSERR
|
---|
144 | && (vaxc$errno == RMS$_ACC || vaxc$errno == RMS$_CRE))
|
---|
145 | errno = EMFILE; /* redirect() should close 1 file & try again */
|
---|
146 |
|
---|
147 | return result;
|
---|
148 | }
|
---|
149 |
|
---|
150 | /*
|
---|
151 | * Check for attempt to (re-)open known file.
|
---|
152 | */
|
---|
153 | /* vms_devopen() - check for "SYS$INPUT" or "SYS$OUTPUT" or "SYS$ERROR" */
|
---|
154 | int
|
---|
155 | vms_devopen( const char *name, int mode )
|
---|
156 | {
|
---|
157 | FILE *file = NULL;
|
---|
158 |
|
---|
159 | if (strncasecmp(name, "SYS$", 4) == 0) {
|
---|
160 | name += 4; /* skip "SYS$" */
|
---|
161 | if (strncasecmp(name, "INPUT", 5) == 0 && (mode & O_WRONLY) == 0)
|
---|
162 | file = stdin, name += 5;
|
---|
163 | else if (strncasecmp(name, "OUTPUT", 6) == 0 && (mode & O_WRONLY) != 0)
|
---|
164 | file = stdout, name += 6;
|
---|
165 | else if (strncasecmp(name, "ERROR", 5) == 0 && (mode & O_WRONLY) != 0)
|
---|
166 | file = stderr, name += 5;
|
---|
167 | if (*name == ':') name++; /* treat trailing colon as optional */
|
---|
168 | }
|
---|
169 | /* note: VAXCRTL stdio has extra level of indirection (*file) */
|
---|
170 | return (file && *file && *name == '\0') ? fileno(file) : -1;
|
---|
171 | }
|
---|
172 |
|
---|
173 |
|
---|
174 | #define VMS_UNITS_PER_SECOND 10000000L /* hundreds of nanoseconds, 1e-7 */
|
---|
175 | #define UNIX_EPOCH "01-JAN-1970 00:00:00.00"
|
---|
176 |
|
---|
177 | extern U_Long sys$bintim(), sys$gettim();
|
---|
178 | extern U_Long lib$subx(), lib$ediv();
|
---|
179 |
|
---|
180 | /*
|
---|
181 | * Get current time in microsecond precision.
|
---|
182 | */
|
---|
183 | /* vms_gettimeofday() - get current time in `struct timeval' format */
|
---|
184 | int
|
---|
185 | vms_gettimeofday(struct timeval *tv, void *timezone__not_used)
|
---|
186 | {
|
---|
187 | /*
|
---|
188 | Emulate unix's gettimeofday call; timezone argument is ignored.
|
---|
189 | */
|
---|
190 | static const Dsc epoch_dsc = { sizeof UNIX_EPOCH - sizeof "", UNIX_EPOCH };
|
---|
191 | static long epoch[2] = {0L,0L}; /* needs one time initialization */
|
---|
192 | const long thunk = VMS_UNITS_PER_SECOND;
|
---|
193 | long now[2], quad[2];
|
---|
194 |
|
---|
195 | if (!epoch[0]) sys$bintim(&epoch_dsc, epoch); /* 1 Jan 0:0:0 1970 */
|
---|
196 | /* get current time, as VMS quadword time */
|
---|
197 | sys$gettim(now);
|
---|
198 | /* convert the quadword time so that it's relative to Unix epoch */
|
---|
199 | lib$subx(now, epoch, quad); /* quad = now - epoch; */
|
---|
200 | /* convert 1e-7 units into seconds and fraction of seconds */
|
---|
201 | lib$ediv(&thunk, quad, &tv->tv_sec, &tv->tv_usec);
|
---|
202 | /* convert fraction of seconds into microseconds */
|
---|
203 | tv->tv_usec /= (VMS_UNITS_PER_SECOND / 1000000);
|
---|
204 |
|
---|
205 | return 0; /* success */
|
---|
206 | }
|
---|
207 |
|
---|
208 |
|
---|
209 | #ifndef VMS_V7
|
---|
210 | /*
|
---|
211 | * VMS prior to V7.x has no timezone support unless DECnet/OSI is used.
|
---|
212 | */
|
---|
213 | /* these are global for use by missing/strftime.c */
|
---|
214 | char *tzname[2] = { "local", "" };
|
---|
215 | int daylight = 0, timezone = 0, altzone = 0;
|
---|
216 |
|
---|
217 | /* tzset() -- dummy to satisfy linker */
|
---|
218 | void tzset(void)
|
---|
219 | {
|
---|
220 | return;
|
---|
221 | }
|
---|
222 | #endif /*VMS_V7*/
|
---|
223 |
|
---|
224 |
|
---|
225 | #ifndef CRTL_VER_V731
|
---|
226 | /* getpgrp() -- there's no such thing as process group under VMS;
|
---|
227 | * job tree might be close enough to be useful though.
|
---|
228 | */
|
---|
229 | int getpgrp(void)
|
---|
230 | {
|
---|
231 | return 0;
|
---|
232 | }
|
---|
233 | #endif
|
---|
234 |
|
---|
235 | #ifndef __GNUC__
|
---|
236 | void vms_bcopy( const char *src, char *dst, int len )
|
---|
237 | {
|
---|
238 | (void) memcpy(dst, src, len);
|
---|
239 | }
|
---|
240 | #endif /*!__GNUC__*/
|
---|
241 |
|
---|
242 |
|
---|
243 | /*----------------------------------------------------------------------*/
|
---|
244 | #ifdef NO_VMS_ARGS /* real code is in "vms/vms_args.c" */
|
---|
245 | void vms_arg_fixup( int *argc, char ***argv ) { return; } /* dummy */
|
---|
246 | #endif
|
---|
247 |
|
---|
248 | #ifdef NO_VMS_PIPES /* real code is in "vms/vms_popen.c" */
|
---|
249 | FILE *popen( const char *command, const char *mode ) {
|
---|
250 | fatal(" Cannot open pipe `%s' (not implemented)", command);
|
---|
251 | return NULL;
|
---|
252 | }
|
---|
253 | int pclose( FILE *current ) {
|
---|
254 | fatal(" Cannot close pipe #%d (not implemented)", fileno(current));
|
---|
255 | return -1;
|
---|
256 | }
|
---|
257 | int fork( void ) {
|
---|
258 | fatal(" Cannot fork process (not implemented)");
|
---|
259 | return -1;
|
---|
260 | }
|
---|
261 | #endif /*NO_VMS_PIPES*/
|
---|
262 | /*----------------------------------------------------------------------*/
|
---|
263 |
|
---|
264 |
|
---|
265 | /*
|
---|
266 | * The following code is taken from the GNU C preprocessor (cccp.c,
|
---|
267 | * 2.8.1 vintage) where it was used #if VMS. It is only needed for
|
---|
268 | * VAX C and GNU C on VAX configurations; DEC C's run-time library
|
---|
269 | * doesn't have the problem described.
|
---|
270 | *
|
---|
271 | * VMS_fstat() and VMS_stat() were static in cccp.c but need to be
|
---|
272 | * accessible to the whole program here. Also, the special handling
|
---|
273 | * for the null device has been introduced for gawk's benefit, to
|
---|
274 | * prevent --lint mode from giving spurious warnings about /dev/null
|
---|
275 | * being empty if it's used as an input file.
|
---|
276 | */
|
---|
277 |
|
---|
278 | #if defined(VAXC) || (defined(__GNUC__) && !defined(__alpha))
|
---|
279 |
|
---|
280 | /* more VMS hackery */
|
---|
281 | #include <fab.h>
|
---|
282 | #include <nam.h>
|
---|
283 |
|
---|
284 | extern unsigned long sys$parse(), sys$search();
|
---|
285 |
|
---|
286 | /* Work around a VAXCRTL bug. If a file is located via a searchlist,
|
---|
287 | and if the device it's on is not the same device as the one specified
|
---|
288 | in the first element of that searchlist, then both stat() and fstat()
|
---|
289 | will fail to return info about it. `errno' will be set to EVMSERR, and
|
---|
290 | `vaxc$errno' will be set to SS$_NORMAL due yet another bug in stat()!
|
---|
291 | We can get around this by fully parsing the filename and then passing
|
---|
292 | that absolute name to stat().
|
---|
293 |
|
---|
294 | Without this fix, we can end up failing to find header files, which is
|
---|
295 | bad enough, but then compounding the problem by reporting the reason for
|
---|
296 | failure as "normal successful completion." */
|
---|
297 |
|
---|
298 | #undef fstat /* Get back to the library version. */
|
---|
299 |
|
---|
300 | int
|
---|
301 | VMS_fstat (fd, statbuf)
|
---|
302 | int fd;
|
---|
303 | struct stat *statbuf;
|
---|
304 | {
|
---|
305 | int result = fstat (fd, statbuf);
|
---|
306 |
|
---|
307 | if (result < 0)
|
---|
308 | {
|
---|
309 | FILE *fp;
|
---|
310 | char nambuf[NAM$C_MAXRSS+1];
|
---|
311 |
|
---|
312 | if ((fp = fdopen (fd, "r")) != 0 && fgetname (fp, nambuf) != 0)
|
---|
313 | result = VMS_stat (nambuf, statbuf);
|
---|
314 | /* No fclose(fp) here; that would close(fd) as well. */
|
---|
315 | }
|
---|
316 |
|
---|
317 | if (result == 0 /* GAWK addition; fixup /dev/null flags */
|
---|
318 | && (statbuf->st_mode & S_IFREG)
|
---|
319 | && STREQ(statbuf->st_dev, "_NLA0:"))
|
---|
320 | {
|
---|
321 | statbuf->st_mode &= ~S_IFREG;
|
---|
322 | statbuf->st_mode |= S_IFCHR;
|
---|
323 | }
|
---|
324 |
|
---|
325 | return result;
|
---|
326 | }
|
---|
327 |
|
---|
328 | int
|
---|
329 | VMS_stat (name, statbuf)
|
---|
330 | const char *name;
|
---|
331 | struct stat *statbuf;
|
---|
332 | {
|
---|
333 | int result = stat (name, statbuf);
|
---|
334 |
|
---|
335 | if (result < 0)
|
---|
336 | {
|
---|
337 | struct FAB fab;
|
---|
338 | struct NAM nam;
|
---|
339 | char exp_nam[NAM$C_MAXRSS+1], /* expanded name buffer for sys$parse */
|
---|
340 | res_nam[NAM$C_MAXRSS+1]; /* resultant name buffer for sys$search */
|
---|
341 |
|
---|
342 | fab = cc$rms_fab;
|
---|
343 | fab.fab$l_fna = (char *) name;
|
---|
344 | fab.fab$b_fns = (unsigned char) strlen (name);
|
---|
345 | fab.fab$l_nam = (void *) &nam;
|
---|
346 | nam = cc$rms_nam;
|
---|
347 | nam.nam$l_esa = exp_nam, nam.nam$b_ess = sizeof exp_nam - 1;
|
---|
348 | nam.nam$l_rsa = res_nam, nam.nam$b_rss = sizeof res_nam - 1;
|
---|
349 | nam.nam$b_nop = NAM$M_PWD | NAM$M_NOCONCEAL;
|
---|
350 | if (sys$parse (&fab) & 1)
|
---|
351 | {
|
---|
352 | if (sys$search (&fab) & 1)
|
---|
353 | {
|
---|
354 | res_nam[nam.nam$b_rsl] = '\0';
|
---|
355 | result = stat (res_nam, statbuf);
|
---|
356 | }
|
---|
357 | /* Clean up searchlist context cached by the system. */
|
---|
358 | nam.nam$b_nop = NAM$M_SYNCHK;
|
---|
359 | fab.fab$l_fna = 0, fab.fab$b_fns = 0;
|
---|
360 | (void) sys$parse (&fab);
|
---|
361 | }
|
---|
362 | }
|
---|
363 |
|
---|
364 | if (result == 0 /* GAWK addition; fixup /dev/null flags */
|
---|
365 | && (statbuf->st_mode & S_IFREG)
|
---|
366 | && STREQ(statbuf->st_dev, "_NLA0:"))
|
---|
367 | {
|
---|
368 | statbuf->st_mode &= ~S_IFREG;
|
---|
369 | statbuf->st_mode |= S_IFCHR;
|
---|
370 | }
|
---|
371 |
|
---|
372 | return result;
|
---|
373 | }
|
---|
374 | #endif /* VAXC || (__GNUC__ && !__alpha) */
|
---|