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

Defined functions

bflush defined in line 651; used 2 times
check defined in line 660; used 1 times
closef defined in line 157; never used
creatf defined in line 47; never used
dupf defined in line 122; used 1 times
empty defined in line 682; used 1 times
fstatf defined in line 639; never used
initbuf defined in line 230; used 1 times
lseekf defined in line 600; never used
openf defined in line 24; never used
openup defined in line 70; used 6 times
readf defined in line 417; never used
realfd defined in line 399; never used
writef defined in line 517; never used

Defined variables

Sccsid defined in line 1; never used

Defined macros

realfd defined in line 22; used 11 times
Last modified: 1983-06-22
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 1711
Valid CSS Valid XHTML 1.0 Strict