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