applied suggested patch, modulo superseded parts
[p5sagit/p5-mst-13.2.git] / vms / ext / Stdio / Stdio.xs
1 /* VMS::Stdio - VMS extensions to stdio routines 
2  *
3  * Version:  2.2
4  * Author:   Charles Bailey  bailey@newman.upenn.edu
5  * Revised:  18-Jul-1998
6  *
7  */
8
9 #include "EXTERN.h"
10 #include "perl.h"
11 #include "XSUB.h"
12 #include <file.h>
13 #include <iodef.h>
14 #include <rms.h>
15 #include <starlet.h>
16
17 static bool
18 constant(name, pval)
19 char *name;
20 IV *pval;
21 {
22     if (strnNE(name, "O_", 2)) return FALSE;
23
24     if (strEQ(name, "O_APPEND"))
25 #ifdef O_APPEND
26         { *pval = O_APPEND; return TRUE; }
27 #else
28         return FALSE;
29 #endif
30     if (strEQ(name, "O_CREAT"))
31 #ifdef O_CREAT
32         { *pval = O_CREAT; return TRUE; }
33 #else
34         return FALSE;
35 #endif
36     if (strEQ(name, "O_EXCL"))
37 #ifdef O_EXCL
38         { *pval = O_EXCL; return TRUE; }
39 #else
40         return FALSE;
41 #endif
42     if (strEQ(name, "O_NDELAY"))
43 #ifdef O_NDELAY
44         { *pval = O_NDELAY; return TRUE; }
45 #else
46         return FALSE;
47 #endif
48     if (strEQ(name, "O_NOWAIT"))
49 #ifdef O_NOWAIT
50         { *pval = O_NOWAIT; return TRUE; }
51 #else
52         return FALSE;
53 #endif
54     if (strEQ(name, "O_RDONLY"))
55 #ifdef O_RDONLY
56         { *pval = O_RDONLY; return TRUE; }
57 #else
58         return FALSE;
59 #endif
60     if (strEQ(name, "O_RDWR"))
61 #ifdef O_RDWR
62         { *pval = O_RDWR; return TRUE; }
63 #else
64         return FALSE;
65 #endif
66     if (strEQ(name, "O_TRUNC"))
67 #ifdef O_TRUNC
68         { *pval = O_TRUNC; return TRUE; }
69 #else
70         return FALSE;
71 #endif
72     if (strEQ(name, "O_WRONLY"))
73 #ifdef O_WRONLY
74         { *pval = O_WRONLY; return TRUE; }
75 #else
76         return FALSE;
77 #endif
78
79     return FALSE;
80 }
81
82
83 static SV *
84 newFH(FILE *fp, char type) {
85     SV *rv;
86     GV **stashp, *gv = (GV *)NEWSV(0,0);
87     HV *stash;
88     IO *io;
89
90     dTHR;
91     /* Find stash for VMS::Stdio.  We don't do this once at boot
92      * to allow for possibility of threaded Perl with per-thread
93      * symbol tables.  This code (through io = ...) is really
94      * equivalent to gv_fetchpv("VMS::Stdio::__FH__",TRUE,SVt_PVIO),
95      * with a little less overhead, and good exercise for me. :-) */
96     stashp = (GV **)hv_fetch(PL_defstash,"VMS::",5,TRUE);
97     if (!stashp || *stashp == (GV *)&PL_sv_undef) return Nullsv;
98     if (!(stash = GvHV(*stashp))) stash = GvHV(*stashp) = newHV();
99     stashp = (GV **)hv_fetch(GvHV(*stashp),"Stdio::",7,TRUE);
100     if (!stashp || *stashp == (GV *)&PL_sv_undef) return Nullsv;
101     if (!(stash = GvHV(*stashp))) stash = GvHV(*stashp) = newHV();
102
103     /* Set up GV to point to IO, and then take reference */
104     gv_init(gv,stash,"__FH__",6,0);
105     io = GvIOp(gv) = newIO();
106     IoIFP(io) = fp;
107     if (type != '<') IoOFP(io) = fp;
108     IoTYPE(io) = type;
109     rv = newRV((SV *)gv);
110     SvREFCNT_dec(gv);
111     return sv_bless(rv,stash);
112 }
113
114 MODULE = VMS::Stdio  PACKAGE = VMS::Stdio
115
116 void
117 constant(name)
118         char *  name
119         PROTOTYPE: $
120         CODE:
121         IV i;
122         if (constant(name, &i))
123             ST(0) = sv_2mortal(newSViv(i));
124         else
125             ST(0) = &PL_sv_undef;
126
127 void
128 binmode(fh)
129         SV *    fh
130         PROTOTYPE: $
131         CODE:
132             IO *io = sv_2io(fh);
133             FILE *fp = io ? IoOFP(io) : NULL;
134             char iotype = io ? IoTYPE(io) : '\0';
135             char filespec[NAM$C_MAXRSS], *acmode, *s, *colon, *dirend = Nullch;
136             int ret = 0, saverrno = errno, savevmserrno = vaxc$errno;
137             fpos_t pos;
138             if (fp == NULL || strchr(">was+-|",iotype) == Nullch) {
139               set_errno(EBADF); set_vaxc_errno(SS$_IVCHAN); XSRETURN_UNDEF;
140             }
141             if (!fgetname(fp,filespec)) XSRETURN_UNDEF;
142             for (s = filespec; *s; s++) {
143               if (*s == ':') colon = s;
144               else if (*s == ']' || *s == '>') dirend = s;
145             }
146             /* Looks like a tmpfile, which will go away if reopened */
147             if (s == dirend + 3) {
148               set_errno(EBADF); set_vaxc_errno(RMS$_IOP); XSRETURN_UNDEF;
149             }
150             /* If we've got a non-file-structured device, clip off the trailing
151              * junk, and don't lose sleep if we can't get a stream position.  */
152             if (dirend == Nullch) *(colon+1) = '\0'; 
153             if (iotype != '-' && (ret = fgetpos(fp, &pos)) == -1 && dirend)
154               XSRETURN_UNDEF;
155             switch (iotype) {
156               case '<': case 'r':           acmode = "rb";                      break;
157               case '>': case 'w': case '|':
158                 /* use 'a' instead of 'w' to avoid creating new file;
159                    fsetpos below will take care of restoring file position */
160               case 'a':                     acmode = "ab";                      break;
161               case '+':  case 's':          acmode = "rb+";                     break;
162               case '-':                     acmode = fileno(fp) ? "ab" : "rb";  break;
163               /* iotype'll be null for the SYS$INPUT:/SYS$OUTPUT:/SYS$ERROR: files */
164               /* since we didn't really open them and can't really */
165               /* reopen them */
166               case 0:                       XSRETURN_UNDEF;
167               default:
168                 if (PL_dowarn) warn("Unrecognized iotype %c for %s in binmode",
169                                  iotype, filespec);
170                 acmode = "rb+";
171             }
172             if (freopen(filespec,acmode,fp) == NULL) XSRETURN_UNDEF;
173             if (iotype != '-' && ret != -1 && fsetpos(fp,&pos) == -1) XSRETURN_UNDEF;
174             if (ret == -1) { set_errno(saverrno); set_vaxc_errno(savevmserrno); }
175             XSRETURN_YES;
176
177
178 void
179 flush(fp)
180         FILE *  fp
181         PROTOTYPE: $
182         CODE:
183             if (fflush(fp)) { ST(0) = &PL_sv_undef; }
184             else            { clearerr(fp); ST(0) = &PL_sv_yes; }
185
186 char *
187 getname(fp)
188         FILE *  fp
189         PROTOTYPE: $
190         CODE:
191             char fname[NAM$C_MAXRSS+1];
192             ST(0) = sv_newmortal();
193             if (fgetname(fp,fname) != NULL) sv_setpv(ST(0),fname);
194
195 void
196 rewind(fp)
197         FILE *  fp
198         PROTOTYPE: $
199         CODE:
200             ST(0) = rewind(fp) ? &PL_sv_undef : &PL_sv_yes;
201
202 void
203 remove(name)
204         char *name
205         PROTOTYPE: $
206         CODE:
207             ST(0) = remove(name) ? &PL_sv_undef : &PL_sv_yes;
208
209 void
210 setdef(...)
211         PROTOTYPE: @
212         CODE:
213             char vmsdef[NAM$C_MAXRSS+1], es[NAM$C_MAXRSS], sep;
214             unsigned long int retsts;
215             struct FAB deffab = cc$rms_fab;
216             struct NAM defnam = cc$rms_nam;
217             struct dsc$descriptor_s dirdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
218             STRLEN n_a;
219             if (items) {
220                 SV *defsv = ST(items-1);  /* mimic chdir() */
221                 ST(0) = &PL_sv_undef;
222                 if (!SvPOK(defsv)) { SETERRNO(EINVAL,LIB$_INVARG); XSRETURN(1); }
223                 if (tovmsspec(SvPV(defsv,n_a),vmsdef) == NULL) { XSRETURN(1); }
224                 deffab.fab$l_fna = vmsdef; deffab.fab$b_fns = strlen(vmsdef);
225             }
226             else {
227                 deffab.fab$l_fna = "SYS$LOGIN"; deffab.fab$b_fns = 9;
228                 EXTEND(sp,1);  ST(0) = &PL_sv_undef;
229             }
230             defnam.nam$l_esa = es;  defnam.nam$b_ess = sizeof es;
231             deffab.fab$l_nam = &defnam;
232             retsts = sys$parse(&deffab,0,0);
233             if (retsts & 1) {
234                 if (defnam.nam$v_wildcard) retsts = RMS$_WLD;
235                 else if (defnam.nam$b_name || defnam.nam$b_type > 1 ||
236                      defnam.nam$b_ver > 1) retsts = RMS$_DIR;
237                 }
238             defnam.nam$b_nop |= NAM$M_SYNCHK; defnam.nam$l_rlf = NULL; deffab.fab$b_dns = 0;
239             if (!(retsts & 1)) {
240                 set_vaxc_errno(retsts);
241                 switch (retsts) {
242                     case RMS$_DNF:
243                         set_errno(ENOENT); break;
244                     case RMS$_SYN: case RMS$_DIR: case RMS$_DEV:
245                         set_errno(EINVAL); break;
246                     case RMS$_PRV:
247                         set_errno(EACCES); break;
248                     default:
249                         set_errno(EVMSERR); break;
250                 }
251                 (void) sys$parse(&deffab,0,0);  /* free up context */
252                 XSRETURN(1);
253             }
254             sep = *defnam.nam$l_dir;
255             *defnam.nam$l_dir = '\0';
256             my_setenv("SYS$DISK",defnam.nam$b_node ? defnam.nam$l_node : defnam.nam$l_dev);
257             *defnam.nam$l_dir = sep;
258             dirdsc.dsc$a_pointer = defnam.nam$l_dir; dirdsc.dsc$w_length = defnam.nam$b_dir;
259             if ((retsts = sys$setddir(&dirdsc,0,0)) & 1) ST(0) = &PL_sv_yes;
260             else { set_errno(EVMSERR); set_vaxc_errno(retsts); }
261             (void) sys$parse(&deffab,0,0);  /* free up context */
262
263 void
264 sync(fp)
265         FILE *  fp
266         PROTOTYPE: $
267         CODE:
268             if (fsync(fileno(fp))) { ST(0) = &PL_sv_undef; }
269             else                   { clearerr(fp); ST(0) = &PL_sv_yes; }
270
271 char *
272 tmpnam()
273         PROTOTYPE:
274         CODE:
275             char fname[L_tmpnam];
276             ST(0) = sv_newmortal();
277             if (tmpnam(fname) != NULL) sv_setpv(ST(0),fname);
278
279 void
280 vmsopen(spec,...)
281         char *  spec
282         PROTOTYPE: @
283         CODE:
284             char *args[8],mode[3] = {'r','\0','\0'}, type = '<';
285             register int i, myargc;
286             FILE *fp;
287             STRLEN n_a;
288         
289             if (!spec || !*spec) {
290                SETERRNO(EINVAL,LIB$_INVARG);
291                XSRETURN_UNDEF;
292             }
293             if (items > 9) croak("too many args");
294         
295             /* First, set up name and mode args from perl's string */
296             if (*spec == '+') {
297               mode[1] = '+';
298               spec++;
299             }
300             if (*spec == '>') {
301               if (*(spec+1) == '>') *mode = 'a', spec += 2;
302               else *mode = 'w',  spec++;
303             }
304             else if (*spec == '<') spec++;
305             myargc = items - 1;
306             for (i = 0; i < myargc; i++) args[i] = SvPV(ST(i+1),n_a);
307             /* This hack brought to you by C's opaque arglist management */
308             switch (myargc) {
309               case 0:
310                 fp = fopen(spec,mode);
311                 break;
312               case 1:
313                 fp = fopen(spec,mode,args[0]);
314                 break;
315               case 2:
316                 fp = fopen(spec,mode,args[0],args[1]);
317                 break;
318               case 3:
319                 fp = fopen(spec,mode,args[0],args[1],args[2]);
320                 break;
321               case 4:
322                 fp = fopen(spec,mode,args[0],args[1],args[2],args[3]);
323                 break;
324               case 5:
325                 fp = fopen(spec,mode,args[0],args[1],args[2],args[3],args[4]);
326                 break;
327               case 6:
328                 fp = fopen(spec,mode,args[0],args[1],args[2],args[3],args[4],args[5]);
329                 break;
330               case 7:
331                 fp = fopen(spec,mode,args[0],args[1],args[2],args[3],args[4],args[5],args[6]);
332                 break;
333               case 8:
334                 fp = fopen(spec,mode,args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7]);
335                 break;
336             }
337             if (fp != Nullfp) {
338               SV *fh = newFH(fp,(mode[1] ? '+' : (mode[0] == 'r' ? '<' : (mode[0] == 'a' ? 'a' : '>'))));
339               ST(0) = (fh ? sv_2mortal(fh) : &PL_sv_undef);
340             }
341             else { ST(0) = &PL_sv_undef; }
342
343 void
344 vmssysopen(spec,mode,perm,...)
345         char *  spec
346         int     mode
347         int     perm
348         PROTOTYPE: @
349         CODE:
350             char *args[8];
351             int i, myargc, fd;
352             FILE *fp;
353             SV *fh;
354             STRLEN n_a;
355             if (!spec || !*spec) {
356                SETERRNO(EINVAL,LIB$_INVARG);
357                XSRETURN_UNDEF;
358             }
359             if (items > 11) croak("too many args");
360             myargc = items - 3;
361             for (i = 0; i < myargc; i++) args[i] = SvPV(ST(i+3),n_a);
362             /* More fun with C calls; can't combine with above because
363                args 2,3 of different types in fopen() and open() */
364             switch (myargc) {
365               case 0:
366                 fd = open(spec,mode,perm);
367                 break;
368               case 1:
369                 fd = open(spec,mode,perm,args[0]);
370                 break;
371               case 2:
372                 fd = open(spec,mode,perm,args[0],args[1]);
373                 break;
374               case 3:
375                 fd = open(spec,mode,perm,args[0],args[1],args[2]);
376                 break;
377               case 4:
378                 fd = open(spec,mode,perm,args[0],args[1],args[2],args[3]);
379                 break;
380               case 5:
381                 fd = open(spec,mode,perm,args[0],args[1],args[2],args[3],args[4]);
382                 break;
383               case 6:
384                 fd = open(spec,mode,perm,args[0],args[1],args[2],args[3],args[4],args[5]);
385                 break;
386               case 7:
387                 fd = open(spec,mode,perm,args[0],args[1],args[2],args[3],args[4],args[5],args[6]);
388                 break;
389               case 8:
390                 fd = open(spec,mode,perm,args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7]);
391                 break;
392             }
393             i = mode & 3;
394             if (fd >= 0 &&
395                ((fp = fdopen(fd, &("r\000w\000r+"[2*i]))) != Nullfp)) {
396               SV *fh = newFH(fp,"<>++"[i]);
397               ST(0) = (fh ? sv_2mortal(fh) : &PL_sv_undef);
398             }
399             else { ST(0) = &PL_sv_undef; }
400
401 void
402 waitfh(fp)
403         FILE *  fp
404         PROTOTYPE: $
405         CODE:
406             ST(0) = fwait(fp) ? &PL_sv_undef : &PL_sv_yes;
407
408 void
409 writeof(mysv)
410         SV *    mysv
411         PROTOTYPE: $
412         CODE:
413             char devnam[257], *cp;
414             unsigned long int chan, iosb[2], retsts, retsts2;
415             struct dsc$descriptor devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, devnam};
416             IO *io = sv_2io(mysv);
417             FILE *fp = io ? IoOFP(io) : NULL;
418             if (fp == NULL || strchr(">was+-|",IoTYPE(io)) == Nullch) {
419               set_errno(EBADF); set_vaxc_errno(SS$_IVCHAN); XSRETURN_UNDEF;
420             }
421             if (fgetname(fp,devnam) == Nullch) { ST(0) = &PL_sv_undef; XSRETURN(1); }
422             if ((cp = strrchr(devnam,':')) != NULL) *(cp+1) = '\0';
423             devdsc.dsc$w_length = strlen(devnam);
424             retsts = sys$assign(&devdsc,&chan,0,0);
425             if (retsts & 1) retsts = sys$qiow(0,chan,IO$_WRITEOF,iosb,0,0,0,0,0,0,0,0);
426             if (retsts & 1) retsts = iosb[0];
427             retsts2 = sys$dassgn(chan);  /* Be sure to deassign the channel */
428             if (retsts & 1) retsts = retsts2;
429             if (retsts & 1) { ST(0) = &PL_sv_yes; }
430             else {
431               set_vaxc_errno(retsts);
432               switch (retsts) {
433                 case SS$_EXQUOTA:  case SS$_INSFMEM:  case SS$_MBFULL:
434                 case SS$_MBTOOSML: case SS$_NOIOCHAN: case SS$_NOLINKS:
435                 case SS$_BUFFEROVF:
436                   set_errno(ENOSPC); break;
437                 case SS$_ILLIOFUNC: case SS$_DEVOFFLINE: case SS$_NOSUCHDEV:
438                   set_errno(EBADF);  break;
439                 case SS$_NOPRIV:
440                   set_errno(EACCES); break;
441                 default:  /* Includes "shouldn't happen" cases that might map */
442                   set_errno(EVMSERR); break;         /* to other errno values */
443               }
444               ST(0) = &PL_sv_undef;
445             }