Commit | Line | Data |
---|---|---|
0f4556f1 C |
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 | } |