|
|
1.1 root 1: /*
2: VMSIO.C -- replacements for lseek(), read() and close() to allow
3: arbitrary byte seeks with non-stream-lf files.
4:
5: (The original version (file routines) were written to port the
6: unix ``less'' program. This version helps with the elvis port.
7: It may be useful elsewhere to port other utilities.)
8:
9: Written by John Campbell [email protected]. Use as you
10: wish, but leave me some credit for killing myself on this when
11: I was sick one weekend, ok?
12:
13: Also, added vms_rename as a weak replacment for link(). 4/2/91
14: ...and because there was a routine named delete, created a vms_delete.
15:
16: Sigh, Steve wanted pipe stuff, so vms_rpipe(), vms_pread() and
17: vms_pclose() was born. 8/2/91
18:
19: Moved the tty i/o routines into this module as well. vms_open_tty()
20: and vms_ttyread() (ttread) 8/2/91
21: */
22:
23: /*
24: Entry points:
25:
26: FILE I/O
27: vms_close (fd)
28: long vms_lseek (fd, offset, direction)
29: int vms_read (fd, buf, len)
30: int vms_rename (from, to)
31: vms_delete (file)
32:
33: PIPE I/O
34: int vms_rpipe (cmd, fd, input_file)
35: int vms_pread (pfile, buffer, size)
36: int vms_rpclose(pfile)
37:
38: TERMINAL I/O
39: vms_open_tty()
40: vms_ttyread(buf, len, time)
41: */
42: static char *version = "VMSIO, version 1.0";
43:
44: #include <stdio.h>
45: #include <errno.h>
46: #include <perror.h>
47: #define BUFSIZE 4096
48:
49: /* Data and buffers used to implement vms_lseek() and vms_read() */
50: static struct {
51: int type, cur_loc, size, lastbin, maxbin, offset, eob;
52: } fdints[_NFILE] =
53: {{0,0,0,0,0,0,-1},{0,0,0,0,0,0,-1},{0,0,0,0,0,0,-1},{0,0,0,0,0,0,-1},
54: {0,0,0,0,0,0,-1},{0,0,0,0,0,0,-1},{0,0,0,0,0,0,-1},{0,0,0,0,0,0,-1},
55: {0,0,0,0,0,0,-1},{0,0,0,0,0,0,-1},{0,0,0,0,0,0,-1},{0,0,0,0,0,0,-1},
56: {0,0,0,0,0,0,-1},{0,0,0,0,0,0,-1},{0,0,0,0,0,0,-1},{0,0,0,0,0,0,-1},
57: {0,0,0,0,0,0,-1},{0,0,0,0,0,0,-1},{0,0,0,0,0,0,-1},{0,0,0,0,0,0,-1}};
58: static char **fdbufs[_NFILE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
59: typedef struct {
60: int loc, bstart, bend;
61: } seeks;
62: static seeks *fdseeks[_NFILE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
63:
64: /* Intended use for fdseeks: fdseeks[fd][i] for i'th triple */
65:
66: #define G_cur_loc fdints[fd].cur_loc
67: #define G_size fdints[fd].size
68: #define G_eof_seen fdints[fd].eof_seen
69: #define G_offset fdints[fd].offset
70: #define G_eob fdints[fd].eob
71:
72:
73: vms_close (fd)
74: int fd;
75: {
76: if (fd >= 0) {
77: /* Reset fdints[fd] and free any buffers we were using. */
78: fdints[fd].type = 0;
79: fdints[fd].cur_loc = 0;
80: fdints[fd].size = 0;
81: fdints[fd].lastbin = 0;
82: fdints[fd].maxbin = 0;
83: fdints[fd].offset = 0;
84: fdints[fd].eob = -1;
85:
86: if (fdseeks[fd])
87: fdseeks[fd] = free (fdseeks[fd]);
88: if (fdbufs[fd])
89: fdbufs[fd] = free (fdbufs[fd]);
90: }
91: return (close(fd));
92: }
93:
94: long vms_lseek (fd, offset, direction)
95: int fd, offset, direction;
96: {
97: int tmp;
98:
99: if (fd > _NFILE) {
100: fprintf (stderr,"Too many files for vms_lseek\n");
101: exit(2);
102: }
103: if (fd < 0) {
104: return lseek (fd, offset, direction);
105: }
106: if (fdints[fd].type == 0)
107: _init(fd);
108: if (fdints[fd].type == 1)
109: return lseek (fd, offset, direction);
110:
111: /* Convert the possibly relative `offset' to an absolute offset. */
112: if (direction == 1) {
113: offset = G_cur_loc + offset + G_offset;
114: }
115: else if (direction == 2) {
116: if (G_eob == -1)
117: _refill (fd, -1); /* Figure out the correct eob byte count */
118: offset = G_eob + offset;
119: }
120: if (offset < 0)
121: return -1;
122:
123: /* Limitation of this implementation--only seeks to end of file. */
124: if (G_eob > -1 && offset > G_eob)
125: offset = G_eob;
126:
127: if (G_offset > offset || offset >= G_offset + G_size) {
128: if (_refill (fd, offset) == -1)
129: return -1;
130: }
131:
132: /* Byte we want is now in our current buffer. */
133: G_cur_loc = offset - G_offset;
134:
135: /* Sanity check. */
136: if (G_cur_loc < 0 || G_cur_loc > BUFSIZE)
137: _error ("G_cur_loc error in vms_lseek\n", 1);
138:
139: return G_cur_loc + G_offset; /* Byte location in current buffer */
140: }
141:
142:
143: int vms_read (fd, buf, len)
144: int fd, len;
145: char *buf;
146: {
147: /* Buffer the read as the unix system would do. */
148: int fill = 0, n, num = 0,tmp;
149: char *buffer;
150:
151: if (fd > _NFILE) {
152: fprintf (stderr,"Too many files for vms_read\n");
153: exit(2);
154: }
155:
156: if (fdints[fd].type == 0)
157: _init(fd);
158: if (fdints[fd].type == 1)
159: return read (fd, buf, len);
160:
161: buffer = fdbufs[fd];
162:
163: if (G_eob != -1 && G_cur_loc + G_offset >= G_eob)
164: return 0; /* EOF */
165:
166: /* Move any buffered data into place. */
167: while (len && G_cur_loc < G_size) {
168: *buf++ = buffer[G_cur_loc++];
169: --len;
170: ++num;
171: }
172: /* Refill as many buffers as necessary to put len bytes into buf. */
173: do {
174: /* Don't refill when asked to read beyond the end of the file. */
175: if (G_cur_loc == G_size && (G_eob == -1 || G_offset + G_size < G_eob)) {
176: if (_refill (fd, G_offset + G_size) == -1) return -1;
177: }
178: while (len && G_cur_loc < G_size) {
179: *buf++ = buffer[G_cur_loc++];
180: --len;
181: ++num;
182: }
183: } while (len && (G_eob == -1 || G_offset + G_size < G_eob));
184: /* Sanity check. */
185: if (G_cur_loc < 0 || G_cur_loc > BUFSIZE)
186: _error ("G_cur_loc bad in vms_read", 1);
187:
188: return num;
189: }
190:
191: /*
192: Fill from the appropriate buffer. Note that offset is a true byte
193: offset and needs to be converted to fdseeks[fd][i].loc before seeking.
194: This technique may, in fact, generalize to other machines.
195: */
196: static int _refill (fd, offset)
197: int fd, offset;
198: {
199: int n, seekbin, tmp;
200: char *buffer = fdbufs[fd];
201:
202: /* See if the offset is in our current buffer (eob offset is a pain). */
203: if (offset > 0 && G_eob > -1 && offset >= G_eob)
204: offset = G_eob - 1;
205:
206: if (offset > 0 && G_offset <= offset && offset < G_offset + G_size)
207: return G_offset;
208:
209: seekbin = fdints[fd].lastbin;
210:
211: /* Now see if the offset is known (already been read once). */
212: if (offset >= 0 && offset < fdseeks[fd][seekbin].bstart) {
213: /* Yes! We can seek to a known point to pick up this offset. */
214: while (seekbin > 0) {
215: if (offset >= fdseeks[fd][seekbin].bstart)
216: break;
217: --seekbin;
218: }
219: }
220: /* Position ourselves for the read (even if we are already there?) */
221: /* We can avoid this lseek if G_offset == [seekbin-1].bstart */
222: if (lseek (fd, fdseeks[fd][seekbin].loc, 0) == -1)
223: _error ("bad seek in _refill");
224:
225: /* Ok, fill this buffer. */
226: more:
227: G_cur_loc = 0;
228: G_size = 0;
229: G_offset = fdseeks[fd][seekbin].bstart;
230:
231: /*
232: Don't want to be left with a partial record. Choices are to shrink
233: down the read size or to keep seeking so we can backup to just before
234: n == read size. Which seems faster? Which puts an arbitrary limit
235: on the record size?
236: */
237: /* Some lines longer than 512 may be truncated. */
238: while (G_size < BUFSIZE - 512 &&
239: (n = read (fd, &buffer[G_size], BUFSIZE - G_size)) > 0) {
240: #ifdef OTHERMETHOD
241: if (n == BUFSIZE - G_size)
242: break; /* Don't count last line (it may be a partial line) */
243: #endif
244: G_size += n;
245: }
246: if (n == 0) {
247: if (G_size == 0)
248: ++G_size;
249: G_eob = G_offset + G_size;
250: offset = G_eob;
251: }
252: else if (n == -1) {
253: _error ("vms_read (_refill) error", 0);
254: return -1;
255: }
256:
257: /* Update the seek array. Finish current bin and start next bin. */
258: if (G_size == 0)
259: ++G_size;
260: tmp = G_offset + G_size - 1;
261: if (seekbin < fdints[fd].lastbin && fdseeks[fd][seekbin].bend != tmp) {
262: fprintf (stderr, "Consistency failure in _refill\n");
263: exit(2);
264: }
265: fdseeks[fd][seekbin].bend = tmp;
266: if (G_eob == -1 || G_offset + G_size < G_eob) {
267: /* Set up the next seek bin */
268: if (++seekbin > fdints[fd].maxbin) {
269: /* Make more room. */
270: fdints[fd].maxbin = 2*fdints[fd].maxbin;
271: fdseeks[fd] = (void *)realloc (fdseeks[fd], fdints[fd].maxbin);
272: }
273:
274: /* Make sure that lastbin is up to date. */
275: if (seekbin > fdints[fd].lastbin) fdints[fd].lastbin = seekbin;
276:
277: if ((tmp = lseek(fd,0,1)) == -1)
278: _error ("Seek error in vms_read (refill)");
279: if (seekbin < fdints[fd].lastbin && fdseeks[fd][seekbin].loc != tmp) {
280: fprintf (stderr, "Consistency failure in _refill\n");
281: exit(2);
282: }
283: fdseeks[fd][seekbin].loc = tmp;
284:
285: if (seekbin >= fdints[fd].lastbin) {
286: fdseeks[fd][seekbin].bstart = G_offset + G_size;
287: fdseeks[fd][seekbin].bend = 0;
288: }
289: else {
290: /* Consistency check */
291: tmp = G_offset + G_size;
292: if (fdseeks[fd][seekbin].bstart != tmp) {
293: fprintf (stderr, "Consistency failure in _refill\n");
294: exit(1);
295: }
296: }
297: }
298: /* Well, it's either a big do-while loop or this goto... */
299: if (offset == -1 ||
300: (offset >= G_offset + G_size && (offset < G_eob || G_eob == -1)))
301: goto more;
302:
303: return G_offset;
304: }
305:
306: #include <types.h>
307: #include <stat.h>
308: /*
309: Note: may want to store a pointer to the statbuf in place of type.
310: For one thing, this would give you the maximum record size.
311:
312: Ok, there are some problems when we don't control open and close.
313: For instance, switching fd's in midstream can throw us. We could
314: store a statbuf pointer to detect this and it may turn out to be
315: necessary later. (statbuf has the fid for the file in it.)
316: */
317: static _init(fd)
318: int fd;
319: {
320: /* Check the file type; allocate a buffer. */
321: struct stat statbuf;
322: int tmp;
323:
324: if (fstat(fd, &statbuf) < 0)
325: _error ("Can't stat\n", 1);
326: /*
327: Some possible file types (from fab.h):
328:
329: rat: FTN - 0 rfm: UDF 0 undefined
330: CR - 1 FIX 1
331: PRN - 2 VAR 2 default type
332: BLK - 3 VFC 3
333: STM 4
334: STMLF 5
335: STMCR 6
336: Also, st_size == 0 seems to indicate a non-file device (mailbox, terminal).
337: */
338: if (statbuf.st_fab_rfm == 5 || statbuf.st_fab_rfm == 0
339: || statbuf.st_size == 0)
340: fdints[fd].type = 1; /* Use 'C' rtl routines directly */
341: else
342: fdints[fd].type = 2; /* Use these routines. */
343:
344: /* Get a buffer for our buffered I/O */
345: fdbufs[fd] = malloc (BUFSIZE);
346:
347: /* Get an array to use for seeking (initial guess). */
348: tmp = (statbuf.st_size + BUFSIZE)/BUFSIZE * sizeof(seeks) + sizeof(seeks);
349: fdseeks[fd] = (void *)malloc (tmp);
350: fdints[fd].maxbin = tmp/sizeof(seeks);
351:
352: /* Fill in the 0'th (first) seeks triplet. */
353: fdseeks[fd][0].loc = 0;
354: fdseeks[fd][0].bstart = 0;
355: fdseeks[fd][0].bend = 0;
356: }
357:
358: #include <descrip.h>
359: static _error (string, leave)
360: char *string;
361: int leave;
362: {
363: char errstr[81];
364: $DESCRIPTOR(errdesc,errstr);
365: short int length;
366:
367: perror (string);
368: if (errno == EVMSERR) {
369: if (SYS$GETMSG(vaxc$errno, &length, &errdesc, 1, 0) == 1) {
370: errstr[length] = '\0';
371: fprintf (stderr, "%s\n", errstr);
372: }
373: }
374: if (leave)
375: exit (44); /* SS_ABORT */
376: }
377: #include <ssdef>
378: #include <iodef>
379: #include <rms>
380: #include <fibdef>
381: #include <atrdef>
382:
383: /* Simply need a trick to hide a global called 'delete' elsewhere. */
384: vms_delete (file)
385: char *file;
386: {
387: delete (file);
388: }
389:
390: /* Pipe routines modified from Chris Janton's (chj) VMS Icon port. */
391: /*
392: Here we fudge to help the "elvis" program implement rpipe. The
393: routine essentially does an popen using fd as stdin--except that
394: few VMS utilities use the 'C' library. So we pass in the standard
395: input file name and use it if fd is non-zero.
396: */
397: #include <dvidef>
398: #include <file>
399:
400: typedef struct _descr {
401: int length;
402: char *ptr;
403: } descriptor;
404:
405: typedef struct _pipe {
406: long pid; /* process id of child */
407: long status; /* exit status of child */
408: long flags; /* LIB$SPAWN flags */
409: int ichan; /* MBX channel number */
410: int ochan;
411: int efn;
412: unsigned running : 1; /* 1 if child is running */
413: } Pipe;
414:
415: Pipe _pipes[_NFILE]; /* one for every open file */
416:
417: #define NOWAIT 1
418: #define NOCLISYM 2
419: #define NOLOGNAM 4
420: #define NOKEYPAD 8
421: #define NOTIFY 16
422: #define NOCONTROL 32
423: #define SFLAGS (NOWAIT|NOKEYPAD|NOCONTROL)
424:
425: int vms_rpipe (cmd, fd, input_file)
426: char *cmd, *input_file;
427: int fd;
428: {
429: int pfile; /* the Pfile */
430: Pipe *pd; /* _pipe database */
431: descriptor inmbxname; /* name of input mailbox */
432: descriptor outmbxname; /* name of mailbox */
433: char inmname[65];
434: char outmname[65]; /* mailbox name string */
435: int ochan; /* mailbox channel number */
436: int ichan; /* Input mailbox channel number */
437: int status; /* system service status */
438: int efn;
439: struct {
440: short len;
441: short code;
442: char *address;
443: char *retlen;
444: int last;
445: } itmlst;
446:
447: if (!cmd || cmd[0] == '\0')
448: return (-1);
449: LIB$GET_EF(&efn);
450: if (efn == -1)
451: return (-1);
452:
453: /* create and open the input mailbox */
454: status = SYS$CREMBX(0, &ichan, 0, 0, 0, 0, 0);
455: if (!(status & 1)) {
456: LIB$FREE_EF(&efn);
457: return (-1);
458: }
459: itmlst.last = inmbxname.length = 0;
460: itmlst.address = inmbxname.ptr = inmname;
461: itmlst.retlen = &inmbxname.length;
462: itmlst.code = DVI$_DEVNAM;
463: itmlst.len = 64;
464: status = SYS$GETDVIW(0, ichan, 0, &itmlst, 0, 0, 0, 0);
465: if (!(status & 1)) {
466: LIB$FREE_EF(&efn);
467: return (-1);
468: }
469: inmname[inmbxname.length] = '\0';
470:
471: /* create and open the output mailbox */
472: status = SYS$CREMBX(0, &ochan, 0, 0, 0, 0, 0);
473: if (!(status & 1)) {
474: LIB$FREE_EF(&efn);
475: return (-1);
476: }
477: itmlst.last = outmbxname.length = 0;
478: itmlst.address = outmbxname.ptr = outmname;
479: itmlst.retlen = &outmbxname.length;
480: status = SYS$GETDVIW(0, ochan, 0, &itmlst, 0, 0, 0, 0);
481: if (!(status & 1)) {
482: LIB$FREE_EF(&efn);
483: return (-1);
484: }
485: outmname[outmbxname.length] = '\0';
486: pfile = open(outmname, O_RDONLY);
487: if (pfile < 0) {
488: LIB$FREE_EF(&efn);
489: SYS$DASSGN(ichan);
490: SYS$DASSGN(ochan);
491: return (-1);
492: }
493: /* Save file information now */
494: pd = &_pipes[pfile]; /* get Pipe pointer */
495: pd->pid = pd->status = pd->running = 0;
496: pd->flags = SFLAGS;
497: pd->ichan = ichan;
498: pd->ochan = ochan;
499: pd->efn = efn;
500:
501: /* Initiate the command by writing down the input mailbox (SYS$INPUT). */
502: if (fd > 0) {
503: char *pre_command[132+12];
504: strcpy (pre_command, "DEFINE/USER SYS$INPUT ");
505: strcat (pre_command, input_file);
506: status = sys$qiow(0, ichan, IO$_WRITEVBLK | IO$M_NOW, 0, 0, 0,
507: pre_command, strlen(pre_command), 0, 0, 0, 0);
508: if (!(status & 1)) {
509: LIB$FREE_EF(&efn);
510: SYS$DASSGN(ichan);
511: SYS$DASSGN(ochan);
512: return (-1);
513: }
514: }
515: status = sys$qiow(0, ichan, IO$_WRITEVBLK | IO$M_NOW,
516: 0, 0, 0, cmd, strlen(cmd), 0, 0, 0, 0);
517: if (!(status & 1)) {
518: LIB$FREE_EF(&efn);
519: SYS$DASSGN(ichan);
520: SYS$DASSGN(ochan);
521: return (-1);
522: }
523: status = sys$qiow(0, ichan, IO$_WRITEOF | IO$M_NOW, 0, 0, 0,
524: 0, 0, 0, 0, 0, 0);
525: if (!(status & 1)) {
526: LIB$FREE_EF(&efn);
527: SYS$DASSGN(ichan);
528: SYS$DASSGN(ochan);
529: return (-1);
530: }
531:
532: status = LIB$SPAWN(0,
533: &inmbxname, /* input file */
534: &outmbxname, /* output file */
535: &pd->flags, 0, &pd->pid, &pd->status, &pd->efn, 0, 0, 0, 0);
536: if (!(status & 1)) {
537: LIB$FREE_EF(&efn);
538: SYS$DASSGN(ichan);
539: SYS$DASSGN(ochan);
540: return (-1);
541: } else {
542: pd->running = 1;
543: }
544: return (pfile);
545: }
546:
547: int vms_pread (pfile, buffer, size)
548: int pfile, size;
549: char *buffer;
550: /* Be compatible when we read data in (handle newlines). */
551: {
552: Pipe *pd;
553: int status, request;
554: struct {
555: short status,
556: count;
557: int :16;
558: } iosb;
559:
560: pd = pfile >= 0 ? &_pipes[pfile] : 0;
561: if (pd == NULL) return -1;
562:
563: /*
564: This is sort of nasty. The default mailbox size is 256 maxmsg
565: and 1056 bufquo if your sysgen parameters are standard. Asking
566: for more on the CREMBX command (in rpipe) might be a bad idea as
567: that could cause an "exceeded quota" error. Since we only return
568: -1 on error, there's no hope that the poor user would ever know
569: what went wrong.
570: */
571: request = size > 256 ? 256 : size - 1;
572: status = sys$qiow(0, pd->ochan, IO$_READVBLK, &iosb, 0, 0,
573: buffer, request, 0, 0, 0, 0);
574: if (!(status & 1)) return -1;
575: if (iosb.status == SS$_ENDOFFILE)
576: return 0;
577: buffer[iosb.count] = '\n';
578: return iosb.count+1;
579: }
580:
581: /*
582: * Taken from pclose - close a pipe
583: * Last modified 2-Apr-86/chj
584: *
585: */
586: int vms_rpclose(pfile)
587: int pfile;
588: {
589: Pipe *pd;
590: int status;
591: int fstatus;
592:
593: pd = pfile >= 0 ? &_pipes[pfile] : 0;
594: if (pd == NULL)
595: return (-1);
596: fstatus = close(pfile);
597: SYS$DASSGN(pd->ichan);
598: SYS$DASSGN(pd->ochan);
599: LIB$FREE_EF(&pd->efn);
600: pd->running = 0;
601: return (fstatus);
602: }
603:
604: /* Terminal routines. */
605:
606: static int tty;
607: static int VMS_term_chan;
608: int VMS_read_raw; /* Set by curses.c in elvis. */
609:
610: #define VMSCheck(a) {int _s; if (~(_s = (a)) & 1) vms_sys$exit (_s);}
611:
612: vms_open_tty()
613: {
614: int c;
615:
616: $DESCRIPTOR(_terminal,"SYS$INPUT");
617: if (VMS_term_chan == 0) {
618: VMSCheck(SYS$ASSIGN (&_terminal, &VMS_term_chan, 0, 0));
619: }
620: /* Was 2 -- jdc */
621: tty = 0;
622:
623: /* Could reset the scrolling region, setup LINES, COLS, etc. */
624:
625: read(tty,&c,0); /* Flush the tty buffer. */
626: }
627:
628: /*
629: * Get a character from the keyboard.
630: */
631: /*ARGSUSED*/
632: vms_ttyread(buf, len, time)
633: char *buf;
634: int len;
635: int time;
636: {
637: char c;
638: int result, terminat[2] = {0,0};
639: struct {
640: short int status; /* I/O completion status */
641: short int bcount; /* byte transfer count */
642: int dev_dep_data; /* device dependant data */
643: } iosb; /* This is a QIO I/O Status Block */
644:
645:
646: if (VMS_read_raw) {
647: /* read(tty,&c,0); */ /* Flush the tty buffer. (needed?) */
648: if (time == 0) {
649: VMSCheck(sys$qiow(0, VMS_term_chan,
650: IO$_READVBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TRMNOECHO,
651: &iosb, 0, 0, buf, 1, 0, terminat, 0, 0));
652: }
653: else {
654: VMSCheck(sys$qiow(0, VMS_term_chan,
655: IO$_READVBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TRMNOECHO | IO$M_TIMED,
656: &iosb, 0, 0, buf, 1, time, terminat, 0, 0));
657: }
658: if (iosb.status == SS$_ENDOFFILE)
659: vms_sys$exit(SS$_ABORT);
660: else if (iosb.status == SS$_TIMEOUT)
661: return 0; /* Timeout */
662: return 1; /* iosb.bcount; */
663: }
664: else {
665: /* I don't believe we ever use ttyread to read in non-raw mode... */
666: #undef read
667: return read (0, buf, len);
668: }
669: }
670:
671: vms_sys$exit(val)
672: {
673: sys$exit(val); /* Debugger entry point. */
674: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.