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: }