BSD 4_3 release
[unix-history] / usr / contrib / apl / src / ay.c
CommitLineData
0f4556f1
C
1static 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
24openf(fname, mode)
25char *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
47creatf(fname, mode)
48char *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
70openup(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
122dupf(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
157closef(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
230initbuf()
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
278newbuf(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
394recursive: return(newbuf(bufcnt, fd));
395
396}
397
398#ifndef realfd
399realfd(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
417readf(fd, buffer, length)
418char *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
517writef(fd, buffer, length)
518char *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
600long lseekf(fd, p1, p2)
601int fd, p2;
602long 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
639fstatf(fd, stat_buf)
640struct 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
651bflush(){
652
653 register j;
654
655 /* Flush all buffers */
656
657 for(j=0; j<NBUF; j++) newbuf(j, -1);
658}
659
660check()
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
682empty(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}