|
|
1.1 root 1: static char Sccsid[] = "ay.c @(#)ay.c 1.1 10/1/82 Berkeley ";
2: #
3:
4: /*
5: * APL Buffered I/O routines
6: *
7: *
8: * The following routines perform all of the I/O required by APL.
9: * They are designed to maximize buffer utilization while maintaining
10: * reliable operation. For a machine such as the PDP-11, the number
11: * of buffers, defined in apl.h, should be around 4 for best results.
12: * For a machine such as the VAX 11/780, a larger number may be desirable
13: * for better results.
14: *
15: */
16:
17: #include "apl.h" /* Header definitions */
18: #ifdef NBUF /* Don't do any of this unless buffering
19: * was requested by defining NBUF
20: */
21:
22: #define realfd(x) files[x].fd_dup
23:
24: openf(fname, mode)
25: char *fname;
26: {
27:
28: int fd;
29:
30:
31: /* The first order of business is to open the
32: * file. If this fails, return -1.
33: */
34:
35: if ((fd=open(fname,mode)) < 0) return(fd);
36:
37:
38: /* Now, perform all of the work necessary for setting
39: * up the file tables. This code is shared by "openf"
40: * and "creatf" since both open files.
41: */
42:
43: openup(fd);
44: return(fd);
45: }
46:
47: creatf(fname, mode)
48: char *fname;
49: {
50:
51: int fd;
52:
53:
54: /* The first order of business is to create the
55: * file. If this fails, return -1.
56: */
57:
58: if ((fd=creat(fname,mode)) < 0) return(fd);
59:
60:
61: /* Now, perform all of the work necessary for setting
62: * up the file tables. This code is shared by "openf"
63: * and "creatf" since both open files.
64: */
65:
66: openup(fd);
67: return(fd);
68: }
69:
70: openup(fd)
71: {
72: struct stat stat_buf;
73: register struct fds *p, *q;
74: register j;
75:
76:
77: /* Try to perform an "fstat" call on the file. This
78: * information is required for all file descriptors
79: * to prevent reading from a file while data destined
80: * for that file is still buffered for output. If for
81: * some reason the "fstat" fails, arbitrarily set
82: * the major/minor number and inode number to zero.
83: */
84:
85: if (fstat(fd, &stat_buf) < 0){
86: write(2, "Can't \"stat\" new fd\n", 20);
87: stat_buf.st_dev = 0;
88: stat_buf.st_ino = 0;
89: }
90:
91:
92: /* Copy the information into the "files" structure, which
93: * maintains such data on all open files.
94: */
95:
96: p = &files[fd];
97: p->fd_dev = stat_buf.st_dev; /* Major/minor device numbers */
98: p->fd_ind = stat_buf.st_ino; /* Inode number */
99: p->fd_open = 1; /* File is open */
100: p->fd_buf = -1; /* No buffer is currently assigned */
101: p->fd_dup = fd; /* All it duplicates is itself */
102: p->fd_lastop = 0; /* Lastop was read (default) */
103: p->fd_pipe = !!lseek(fd, 0L, 1); /* Seek will fail if a pipe */
104:
105:
106: /* Determine if the new fd is unique or not. If not,
107: * make sure any matching fd's know that they no longer
108: * are either.
109: */
110:
111: p->fd_uniq = 1; /* Assume uniqueness */
112: for(j=0; j<NFDS; j++){
113: if (!files[j].fd_open) continue; /* File not open */
114:
115: q = &files[realfd(j)];
116: if (p->fd_ind == q->fd_ind && p->fd_dev == q->fd_dev)
117: p->fd_uniq = q->fd_uniq = 0;
118: }
119: check(); /*DEBUG*/
120: }
121:
122: dupf(fd)
123: {
124:
125: int fd2;
126: register struct fds *p, *q;
127:
128:
129: /* Duplicating a file descriptor does not require an "fstat"
130: * on the new file descriptor, because its device and inode
131: * are already recorded. However, duplicate file descriptors
132: * complicate I/O because they MUST be linked to the same
133: * buffer.
134: */
135:
136: if ((fd2=dup(fd)) < 0) return(fd2); /* Get new fd */
137:
138: /* Copy all of the information into the "files" for the new
139: * file descriptor. The uniqueness of the original fd is copied
140: * to the new fd -- they both represent the same thing.
141: */
142:
143: p = &files[fd]; /* Point to appropriate places */
144: q = &files[fd2];
145:
146: q->fd_dev = p->fd_dev; /* Major/minor device number */
147: q->fd_ind = p->fd_ind; /* Inode number */
148: q->fd_buf = -1; /* Buffer is assigned to princ. fd */
149: q->fd_uniq = p->fd_uniq; /* Uniqueness of dev/inode */
150: q->fd_dup = p->fd_dup; /* Point new entry to old one */
151: q->fd_open = 1; /* File is now open */
152: q->fd_lastop = p->fd_lastop; /* Last operation is the same */
153:
154: return(fd2); /* Return new fd */
155: }
156:
157: closef(fd)
158: {
159:
160: register j;
161: register struct fds *p, *q;
162: int fd2, count;
163:
164: /* Closing a file is easier than opening it, but some precautions
165: * are necessary. For instance, if a "dup" was performed to
166: * obtain some new file descriptor and the original file is
167: * now being closed, the duplicated file must retain all
168: * pertinent information. Thus, the first order of business
169: * is to scan the list of file descriptors for duplicates.
170: */
171:
172: p = &files[fd]; /* Entry for dying fd */
173:
174: count = 0; /* Number of remaining dups */
175: fd2 = -1; /* New fd reference for dups */
176:
177: if (p->fd_dup == fd) for (j=0; j<NFDS; j++){
178: if (j == fd) continue; /* Don't look at fd */
179: q = &files[j];
180: if (!q->fd_open) continue; /* Not open */
181: if (q->fd_dup != fd) continue;
182: if (fd2 == -1){
183: fd2 = j; /* New reference */
184: q->fd_buf = p->fd_buf; /* Vital data */
185: q->fd_open = 1;
186: q->fd_lastop = p->fd_lastop;
187: }
188: q->fd_dup = fd2;
189: count++;
190: }
191:
192:
193: /* Flush and release the buffer associated with this fd. */
194:
195: newbuf(files[realfd(fd)].fd_buf, -1);
196:
197:
198: /* Mark the entry in the file descriptor table as "closed"
199: * and check the uniqueness of any remaining fd's pointing to
200: * the same area. Be sure the buffer is released.
201: */
202:
203: p->fd_open = 0;
204: p->fd_dup = fd;
205: p->fd_buf = -1;
206:
207: for(j=0; j<NFDS; j++){
208: if (j == fd) continue; /* Skip the same fd */
209: q = &files[fd];
210: if (!q->fd_open) continue; /* Skip closed files */
211: if (p->fd_dev == q->fd_dev && p->fd_ind == q->fd_ind) break;
212: }
213:
214: if (j<NFDS) q->fd_uniq = 1; /* Assume new fd is unique */
215: while(++j < NFDS){ /* Now check to be sure */
216: p = &files[j];
217: if (p->fd_dev == q->fd_dev && p->fd_ind == q->fd_ind)
218: p->fd_uniq = q->fd_uniq = 0;
219: }
220:
221:
222: /* Actually perform the close of the fd, and return the status
223: * of that system call.
224: */
225:
226: return(close(fd));
227: }
228:
229:
230: initbuf()
231: {
232:
233: /* Initialize buffer structure, then use "openup" to set up
234: * file descriptors 0, and 1, which should already be
235: * open from parent process. If 0 and 1 have the same inode
236: * and major/minor number they will be considered to be
237: * duplicates. Fils descriptor 2 will be closed and set to
238: * duplicate file descriptor 1.
239: */
240:
241: register j;
242: register struct fds *p, *q;
243:
244:
245: /* Initialize file descriptor table */
246:
247: for(j=0; j<NFDS; j++){
248: files[j].fd_open = 0; /* Closed */
249: files[j].fd_dup = j; /* Dup = fd */
250: files[j].fd_buf = -1; /* No buffer */
251: files[j].fd_pipe = 0; /* Not pipe */
252: }
253:
254:
255: /* Initialize buffer table */
256:
257: for(j=0; j<NBUF; j++) iobuf[j].b_fd = -1; /* Available */
258:
259:
260: /* "Open" devices 0, and 1 */
261:
262: openup(0);
263: openup(1);
264: p = &files[0];
265: q = &files[1];
266: if (p->fd_ind == q->fd_ind && p->fd_dev == q->fd_dev){
267: q->fd_dup = 0;
268: p->fd_uniq = q->fd_uniq = 1;
269: }
270:
271:
272: /* File descriptor 2 duplicates fd 1 */
273:
274: close(2);
275: dupf(1);
276: }
277:
278: newbuf(bufnum, fd)
279: {
280:
281: register struct iobuf *bp;
282: register struct fds *fp;
283: register j;
284: static int bufcnt = 0;
285:
286:
287: /* The two arguments for this routine specify a buffer to
288: * be operated upon and a file descriptor. There are three
289: * legal cases:
290: *
291: * bufnum < 0, fd >= 0 -- assign new buffer to file descriptor fd
292: * bufnum >= 0, fd >= 0 -- assign buffer "bufnum" to fd
293: * bufnum >= 0, fd < 0 -- de-assign buffer "bufnum"
294: */
295:
296: if ((bufnum < 0 && fd < 0) || bufnum >= NBUF || fd >= NFDS)
297: return(-1); /* Invalid args */
298:
299: fd = (fd < 0) ? fd : realfd(fd); /* Handle dups */
300:
301:
302: /* Step one -- if a buffer was specified, flush it. If the
303: * last operation was a write, then write out the rest of
304: * the buffer. If the last operation was a read, then perform
305: * a seek backwards on the corresponding fd to reposition the
306: * next read.
307: */
308:
309: if (bufnum >= 0){
310: bp = &iobuf[bufnum];
311: if (bp->b_len && (bp->b_fd >= 0))
312: if (files[bp->b_fd].fd_lastop)
313: write(bp->b_fd, bp->b_buf, bp->b_len);
314: else
315: lseek(bp->b_fd, (long)-bp->b_len, 1);
316:
317: bp->b_len = 0; /* Reset length */
318: bp->b_next = 0; /* Reset next position */
319:
320:
321: /* Step one point five -- if a file descriptor was
322: * specified, check to insure that it is open. Then,
323: * reassign the buffer to that fd.
324: */
325:
326: if(bp->b_fd >= 0) /* Drop old assignment */
327: files[bp->b_fd].fd_buf = -1;
328:
329: bp->b_fd = fd; /* Give buffer new fd */
330: if (fd < 0) return(0); /* If fd < 0, done */
331:
332: fp = &files[fd]; /* Point to new structure */
333:
334: if (!fp->fd_open){ /* New file is not open */
335: bp->b_fd = -1; /* Drop assignment */
336: return(-1);
337: }
338:
339: fp->fd_buf = bufnum; /* New assignment */
340: return(0);
341:
342: }
343:
344:
345: /* Step two -- if no buffer was specified, but a file descriptor
346: * was, then some existing buffer must be allocated to that fd.
347: */
348:
349: fp = &files[fd];
350:
351:
352: /* First, check to see if a buffer is already assigned */
353:
354: for (j=0; j<NBUF; j++)
355: if (iobuf[j].b_fd == fd){
356: bufcnt = j;
357: goto recursive;
358: }
359:
360:
361: /* Next, scan the table of buffers looking for one
362: * which is not assigned.
363: */
364:
365: for (j=0; j<NBUF; j++) if (iobuf[j].b_fd < 0){
366: bufcnt = j;
367: goto recursive;
368: }
369:
370:
371: /* If no vacant buffer was found, then a buffer must
372: * be allocated arbitrarily. The static local variable
373: * "bufcnt" is used for this purpose. It contains the
374: * number of the last assigned buffer. Buffers are
375: * switched in a sort-of "round robin" approach.
376: */
377:
378: j = bufcnt;
379: do {
380: bp = &iobuf[j];
381: fp = &files[bp->b_fd];
382: if (fp->fd_pipe && (!fp->fd_lastop) && bp->b_len > 0)
383: continue;
384: bufcnt = j;
385: goto recursive;
386: } while((j = (j+1)%NBUF) != bufcnt);
387: return(-1);
388:
389:
390: /* Now, with a recursive call to this routine,
391: * switch to the new buffer.
392: */
393:
394: recursive: return(newbuf(bufcnt, fd));
395:
396: }
397:
398: #ifndef realfd
399: realfd(fd)
400: {
401:
402: register struct fds *fp;
403:
404:
405: /* Process duplicate file descriptors. If the main fd is
406: * nonnegative, use the value of the dup number for the true fd.
407: * No change is made if the file is closed.
408: */
409:
410: fp = &files[fd];
411: if (!fp->fd_open) return(fd);
412: return(fp->fd_dup < 0 ? fd : fp->fd_dup);
413: }
414: #endif
415:
416:
417: readf(fd, buffer, length)
418: char *buffer;
419: {
420:
421: register struct fds *fp;
422: register struct iobuf *bp;
423: register j;
424: int change, trlog, again;
425:
426:
427: /* Handle duplicate files first. If this file descriptor is
428: * a duplicate of another one, then the other's buffer and
429: * other information must be used.
430: */
431:
432: fd = realfd(fd);
433:
434:
435: /* If a read is to be performed on a file, all output to that
436: * file must first be flushed. If the file descriptor's last
437: * function was output, use "newbuf" to flush the output,
438: * retaining the buffer, and then set the mode to input. This
439: * must be done for all entries in "files" which have the same
440: * major/minor number as the current file.
441: */
442:
443:
444:
445: fp = &files[fd];
446: if (!fp->fd_open) return(-1); /* File not open */
447: if (fp->fd_uniq){
448: if (fp->fd_lastop && fp->fd_buf >= 0)
449: newbuf(fp->fd_buf, fd);
450: } else for(j=0; j<NFDS; j++){
451: fp = &files[realfd(j)];
452: if (fp->fd_dev == files[fd].fd_dev
453: && fp->fd_ind == files[fd].fd_ind
454: && fp->fd_open
455: && fp->fd_lastop
456: && fp->fd_buf >= 0){
457: newbuf(fp->fd_buf, j);
458: }
459: }
460:
461:
462: /* The above code nicely took care of any buffer associated
463: * with the current fd. Now, set the last operation on this fd
464: * to read, and if no buffer is currently assigned, get one.
465: */
466:
467: fp = &files[fd];
468: fp->fd_lastop = 0;
469:
470: if (fp->fd_buf < 0)
471: if (newbuf(-1, fd) < 0) return(read(fd, buffer, length));
472:
473:
474: /* The flag "again" determines whether or not the read buffer
475: * should be refilled when the end of it is reached. Basically,
476: * "again" is set to 0 when a read of less than one buffer length
477: * occurs. This way, terminal reads can stop at the carriage
478: * return, but disc reads can still buffer in BLEN-byte chunks
479: */
480:
481: again = 1;
482:
483:
484: /* The variable "trlog" keeps track of how many bytes have been
485: * transferred into the buffer supplied by the routine which
486: * called "readf". Initially, this number is -1 (EOF).
487: */
488:
489: trlog = -1;
490:
491:
492: /* The main loop is rather simple -- the buffer is continually
493: * emptied and refilled until all of the requested data has been
494: * transferred.
495: */
496:
497: bp = &iobuf[fp->fd_buf];
498: while(1){
499: if (length <= 0) return(trlog);
500: if (bp->b_len == 0 && again){
501: again = (bp->b_len=read(fd,bp->b_buf,BLEN)) == BLEN;
502: bp->b_next = 0;
503: }
504: if (bp->b_len <= 0) return(trlog);
505: if (trlog < 0) trlog++;
506: change = (bp->b_len < length) ? bp->b_len : length;
507: for (j=0; j<change; j++)
508: buffer[trlog+j] = bp->b_buf[bp->b_next+j];
509: trlog += change;
510: bp->b_len -= change;
511: bp->b_next += change;
512: length -= change;
513: }
514: }
515:
516:
517: writef(fd, buffer, length)
518: char *buffer;
519: {
520:
521: register struct fds *fp;
522: register struct iobuf *bp;
523: register j;
524: int change, trlog, again;
525:
526:
527: /* Handle duplicate files first. If this file descriptor is
528: * a duplicate of another one, then the other's buffer and
529: * other information must be used.
530: */
531:
532: fd = realfd(fd);
533:
534:
535: /* If a write is to be performed on a file, all input from that
536: * file must first be flushed. If the file descriptor's last
537: * function was input, use "newbuf" to flush the input,
538: * retaining the buffer, and then set the mode to output. This
539: * must be done for all entries in "files" which have the same
540: * major/minor number as the current file.
541: */
542:
543: fp = &files[fd];
544: if (!fp->fd_open) return(-1); /* File not open */
545: if (fp->fd_uniq){
546: if ((!fp->fd_lastop) && fp->fd_buf >= 0)
547: newbuf(fp->fd_buf, fd);
548:
549: } else for(j=0; j<NFDS; j++){
550: fp = &files[realfd(j)];
551: if (fp->fd_dev == files[fd].fd_dev
552: && fp->fd_ind == files[fd].fd_ind
553: && fp->fd_open
554: && (!fp->fd_lastop)
555: && fp->fd_buf >= 0){
556: newbuf(fp->fd_buf, j);
557: }
558: }
559:
560:
561: /* The above code nicely took care of any buffer associated
562: * with the current fd. Now, set the last operation on this fd
563: * to write, and if no buffer is currently assigned, get one.
564: */
565:
566: fp = &files[fd];
567: fp->fd_lastop = 1;
568:
569: if (fp->fd_buf < 0)
570: if (newbuf(-1, fd) < 0) return(write(fd,buffer,length));
571:
572:
573: /* The main loop of output, like the main loop for input, is
574: * rather simple. In fact, output is easier. It is only
575: * necessary to check when the buffer is full and to flush it
576: * as necessary to the file.
577: */
578:
579: bp = &iobuf[fp->fd_buf];
580: trlog = 0;
581: while(1){
582: if (!length) return(trlog);
583: if (bp->b_len >= BLEN){
584: write(fd, bp->b_buf, bp->b_len);
585: bp->b_next = bp->b_len = 0;
586: }
587:
588: change = ((BLEN - bp->b_len) < length) ?
589: (BLEN - bp->b_len) : length;
590:
591: for (j=0; j<change; j++)
592: bp->b_buf[j+bp->b_next] = buffer[j+trlog];
593:
594: trlog += change;
595: bp->b_next = bp->b_len += change;
596: length -= change;
597: }
598: }
599:
600: long lseekf(fd, p1, p2)
601: int fd, p2;
602: long p1;
603: {
604:
605: register struct fds *fp;
606: register j;
607: long lseek();
608:
609:
610: /* A seek on a file requires a flush of the buffer for that
611: * file and for any other file descriptors which are pointing
612: * to the same file.
613: */
614:
615: fd = realfd(fd); /* Take care of dups */
616: fp = &files[fd];
617:
618: if (!fp->fd_open) return(-1); /* not open */
619:
620: if (fp->fd_uniq){
621: if (fp->fd_buf >= 0) newbuf(fp->fd_buf, fd);
622: } else for(j=0; j<NFDS; j++){
623: fp = &files[j];
624: if (fp->fd_dev == files[fd].fd_dev
625: && fp->fd_ind == files[fd].fd_ind
626: && fp->fd_open
627: && fp->fd_buf >= 0)
628: newbuf(fp->fd_buf, j);
629: }
630:
631:
632: /* Now that all of the preliminaries are over, the actual
633: * seek can be performed.
634: */
635:
636: return(lseek(fd, p1, p2));
637: }
638:
639: fstatf(fd, stat_buf)
640: struct stat *stat_buf;
641: {
642:
643: register fd2;
644:
645: fd2 = realfd(fd);
646: newbuf(files[fd2].fd_buf, fd2);
647: return(fstat(fd, stat_buf));
648:
649: }
650:
651: bflush(){
652:
653: register j;
654:
655: /* Flush all buffers */
656:
657: for(j=0; j<NBUF; j++) newbuf(j, -1);
658: }
659:
660: check()
661: {
662: register j, k;
663:
664: for (j=0; j<NFDS; j++)
665: if ((k=files[j].fd_buf) >= 0)
666: if (iobuf[k].b_fd != j){
667: write(2, "CONSISTENCY CHECK!\n", 19);
668: if (files[j].fd_dup != j)
669: write(2, "BAD DUP ENTRY\n", 14);
670: }
671:
672:
673: for (j=0; j<NBUF; j++)
674: if ((k=iobuf[j].b_fd) >= 0)
675: if (files[k].fd_buf != j)
676: write(2, "CONSISTENCY CHECK2!\n", 20);
677:
678: }
679:
680: #endif
681:
682: empty(fd){
683:
684: struct stat sbuf;
685: register struct fds *fp;
686:
687: /* Simulate the Rand Corp.'s "empty" system call on a
688: * V7 system by seeing if the given fd is a pipe, and if
689: * so, whether or not it is empty.
690: */
691:
692: #ifdef NBUF
693: fp = &files[realfd(fd)];
694:
695: if (!fp->fd_pipe || !fp->fd_open)
696: return(-1); /* Not a pipe */
697:
698: if (fp->fd_buf >= 0 && iobuf[fp->fd_buf].b_len > 0)
699: return(0); /* Data pending in buffer */
700: #endif
701:
702: if (fstat(fd, &sbuf) < 0)
703: return(-1); /* Can't "stat" it */
704:
705: return(sbuf.st_size == 0);
706: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.