|
|
1.1 root 1: static char ID[] = "@(#) tv.c: 1.15 5/1/83";
2:
3: #include <stdio.h>
4:
5: #include "system.h"
6:
7: #if TS || RT
8: #include <ar.h>
9: #else
10: #include <archive.h>
11: #endif
12:
13: #include "reloc.h"
14: #include "sdpsrc/hd/define2.h"
15: #include "structs.h"
16: #include "tv.h"
17: #include "ldtv.h"
18: #include "ldfcn.h"
19: #include "extrns.h"
20: #include "list.h"
21: #include "sgsmacros.h"
22: #include "bool.h"
23: #include "ldmacros.h"
24:
25: static FILE *tvstream = NULL;
26: #if AR16WR
27: static char *tvbuf;
28: #endif
29: static FILE *tvread = NULL;
30: static int tvnxt; /* next available tv slot */
31: long gettvnxt(); /* finds next usable slot */
32: /*eject*/
33: tvspecdef()
34: {
35: #if TRVEC
36: /*
37: * Complete definition of tvspec
38: * called just after reading transfer file
39: */
40:
41: register unsigned spare;
42:
43: #if !ONEPROC
44: tvspec.tvinflnm = (char *) ((int) tvspec.tvinflnm + (int) strbase);
45: #endif
46: spare = (unsigned) tvspec.tvosptr;
47: tvspec.tvosptr = (OUTSECT *) myalloc(sizeof(OUTSECT));
48: tvspec.tvosptr->oshdr.s_size = ((long) spare) * TVENTSZ;
49: if ( aflag ) {
50: /*
51: * Define the Output Section which will be used to
52: * contain the tv
53: */
54: tvspec.tvosptr->osflags = FILL;
55: tvspec.tvosptr->osalign = 16;
56: copy(tvspec.tvosptr->oshdr.s_name, _TV, 8);
57: tvspec.tvosptr->oshdr.s_paddr = -1L; /*none given */
58: if (tvspec.tvrange[0]==0) /* leave slot 0 empty */
59: tvspec.tvosptr->oshdr.s_size += TVENTSZ;
60: }
61:
62: #endif
63: }
64:
65: #if TRVEC
66: tvreloc()
67: {
68:
69: register INSECT *sptr;
70: register int numreloc; /* nbr of relocation entries */
71: register SYMTAB *pglob; /* ptr to ld symbol entry */
72: AUXTAB *paux; /* ptr to auxillary entry */
73: AUXENT aux; /* auxillary entry */
74: SYMENT lsym; /* local symbol table entry */
75: SCNHDR shdr; /* section header */
76: RELOC rentry; /* local relocation entry */
77: LDFILE *rfd,rfdbuf, *ifd, ifdbuf;
78: INFILE *infl, *pnfl;
79: char *symname;
80:
81: /*
82: * the functions in DMERT's shared libraries have 0 in the tvndx
83: * field. To insure that all functions needing tv slots have P_TV
84: * in the tvndx field, scan all relocation entries for references
85: * to functions in shared libraries.
86: */
87:
88: rfd = &rfdbuf;
89: ifd = &ifdbuf;
90: IOPTR(ifd) = NULL;
91: pnfl = NULL;
92: infl = (INFILE *)infilist.head;
93: while (infl != NULL) {
94:
95: OFFSET(ifd) = OFFSET(rfd) = infl->flfiloff;
96: if ( IOPTR(ifd) == NULL) {
97: if ((IOPTR(ifd) = fopen(infl->flname, "r")) == NULL ||
98: (IOPTR(rfd) = fopen(infl->flname, "r")) == NULL )
99: lderror(2,0,NULL, "Can't open %s", infl->flname);
100: if (infl->flfiloff == 0L)
101: TYPE(ifd) = TYPE(rfd) = 0;
102: else
103: TYPE(ifd) = TYPE(rfd) = USH_ARTYPE;
104: }
105: if (TYPE(ifd) == ARTYPE) {
106: if (FSEEK(ifd, 0L, BEGINNING) != OKFSEEK)
107: lderror(2,0,NULL, "internal error: fail to seek to the member of %s",
108: infl->flname);
109: }
110: if (FREAD(&(HEADER(ifd)),FILHSZ,1,ifd) != 1 )
111: lderror(2,0,NULL, "fail to read file header of %s", infl->flname);
112: if (HEADER(ifd).f_flags & F_RELFLG) {
113: pnfl = infl;
114: infl = infl->flnext;
115: continue;
116: }
117:
118: HEADER(rfd) = HEADER(ifd);
119:
120: for( sptr = infl->flishead; sptr != NULL; sptr = sptr->isnext ) {
121: if( (numreloc = sptr->ishdr.s_nreloc) > 0 ) {
122: if (FSEEK(rfd,(long) (FILHSZ+HEADER(rfd).f_opthdr+
123: (sptr->isecnum-1) * SCNHSZ),BEGINNING) != OKFSEEK ||
124: FREAD(&shdr,SCNHSZ,1,rfd) != 1 ||
125: FSEEK(rfd,shdr.s_relptr,BEGINNING) != OKFSEEK) {
126: lderror(1,0,NULL, "Seek to relocation entries for section %.8s in file %s failed",
127: sptr->ishdr.s_name, infl->flname);
128: numreloc = 0;
129: }
130: }
131: while( numreloc-- ) {
132: if (fread(&rentry, RELSZ, 1, IOPTR(rfd)) != 1)
133: lderror(2,0,NULL,"fail to read reloc entries of file %s",
134: infl->flname);
135: if( ! (ISTVRELOC(rentry.r_type)) )
136: continue;
137: if (FSEEK(ifd,HEADER(ifd).f_symptr+
138: rentry.r_symndx * SYMESZ,BEGINNING) != OKFSEEK ||
139: FREAD(&lsym,SYMESZ,1,ifd) != 1)
140: lderror(2,0,NULL,"fail to read symtb entry %ld of file %s",
141: rentry.r_symndx,infl->flname);
142: if ( lsym.n_sclass != C_EXT )
143: continue; /* ignore statics */
144:
145: symname = SYMNAME( lsym );
146: pglob = findsym( symname );
147:
148: if ( pglob->sment.n_scnum > 0) {
149: if( pglob->sment.n_numaux > 0 ) {
150: paux = getaux(pglob->smauxid);
151: if (paux->axent.x_sym.x_tvndx == 0) {
152: paux->axent.x_sym.x_tvndx = P_TV;
153: tvspec.tvosptr->oshdr.s_size += TVENTSZ;
154: PUTAUX(paux, 1);
155: }
156: else
157: PUTAUX(paux, 0);
158: PUTSYM( pglob, 0 );
159: } else {
160: zero(&aux, AUXESZ);
161: aux.x_sym.x_tvndx = P_TV;
162: tvspec.tvosptr->oshdr.s_size += TVENTSZ;
163: putaux( makeaux(pglob, &aux, 0), 1);
164: PUTSYM( pglob, 1 );
165: }
166:
167: } else
168: PUTSYM( pglob, 0 );
169: }
170: }
171:
172: pnfl = infl;
173: infl = infl->flnext;
174:
175: /* see if next input is from the same archive */
176: if ((pnfl->flfiloff > 0L) && (infl != NULL))
177: if (strcmp(pnfl->flname, infl->flname) == 0 &&
178: (pnfl->flfiloff < infl->flfiloff))
179: continue;
180:
181: fclose(IOPTR(ifd));
182: fclose(IOPTR(rfd));
183: IOPTR(ifd) = NULL;
184: }
185:
186: }
187: #endif
188:
189:
190:
191: tvupdat()
192: {
193: #if TRVEC
194: /*
195: * If _TV section is not user-defined but needs to
196: * be, add it to the list of output sections
197: *
198: * Check if _TV section needs to be bonded. If so, add it to
199: * the bondlist
200: */
201:
202: OUTSECT *osptr;
203:
204: if ( (osptr = fndoutsec(_TV)) != NULL ) {
205: ++usrdeftv;
206: /* redo work done in tvspecdef() */
207: free( tvspec.tvosptr );
208: tvspec.tvosptr = osptr;
209: tvspec.tvosptr->osflags = FILL;
210: tvspec.tvosptr->osalign = 16;
211: tvspec.tvosptr->oshdr.s_paddr = -1L;
212: } else {
213: /*
214: * User defined no .tv output section but .tv
215: * input sections may be present
216: */
217: if ( ! aflag )
218: return;
219: if ( usrdeftv ) /* there are .tv input sections */
220: if ( tvspec.tvrange[0] == 0 )
221: tvspec.tvosptr->oshdr.s_size -= TVENTSZ;
222: ++numoutsc;
223: listadd(l_OS, &outsclst, tvspec.tvosptr);
224:
225: if( tvspec.tvbndadr != -1L ) {
226: register ACTITEM *aptr;
227:
228: aptr = (ACTITEM *) myalloc(sizeof(ACTITEM));
229: aptr->bond.aitype = AIBOND;
230: aptr->bond.aiinflnm = tvspec.tvinflnm;
231: aptr->bond.aiinlnno = tvspec.tvinlnno;
232: aptr->bond.aioutsec = tvspec.tvosptr;
233: aptr->bond.aiadrbnd = tvspec.tvbndadr;
234: listadd(l_AI, &bondlist, aptr);
235: }
236: }
237: #endif
238: }
239: /*eject*/
240: tvupdt2()
241: {
242: #if TRVEC
243: OUTSECT *osptr;
244: long *lptr;
245:
246: if ( usrdeftv ) {
247: osptr = fndoutsec(_TV);
248: /*
249: * Assume RANGE(0,N)
250: */
251: tvspec.tvlength = osptr->oshdr.s_size / TVENTSZ;
252: if ( tvspec.tvrange[1] == 0 ) /* no RANGE directive */
253: tvspec.tvrange[1] = tvspec.tvlength - 1;
254: } else {
255: /*
256: * Make sure that TV directive is adequate
257: * and fill in missing spaces
258: */
259:
260: lptr = &tvspec.tvosptr->oshdr.s_size;
261: if ((tvspec.tvrange[1]!=0)
262: &&((long)((tvspec.tvrange[1]-tvspec.tvrange[0]+(tvspec.tvrange[0]? 1 : 2))*TVENTSZ)<*lptr))
263: lderror(2,0,tvspec.tvinflnm, "tv range allows %d entries; %d needed",
264: tvspec.tvrange[1]-tvspec.tvrange[0]+1,
265: (int) (*lptr/TVENTSZ));
266: if( (((long) tvspec.tvlength) * TVENTSZ) >= *lptr )
267: *lptr = ((long) tvspec.tvlength) * TVENTSZ;
268: else if( tvspec.tvlength == 0 )
269: tvspec.tvlength = *lptr / TVENTSZ;
270: else
271: lderror(1,0,NULL, "tv needs %ld entries; only %d allowed",
272: *lptr/TVENTSZ, tvspec.tvlength);
273: if (tvspec.tvrange[1] == 0)
274: tvspec.tvrange[1] = tvspec.tvlength - 1;
275: }
276: #endif
277: }
278: /*eject*/
279: tvinit()
280: {
281: #if TRVEC
282:
283: /*
284: * Initialize for writing the transfer vector
285: */
286:
287: register SYMTAB *pglob;
288: register AUXTAB *paux;
289: register int indx;
290: SCNHDR *psect;
291: TVENTRY outslot;
292: AUXENT aux;
293: long offset; /* where to write slot */
294: int changed;
295:
296:
297: if( (tvstream = fopen(outfilnm, "r+")) == NULL )
298: lderror(2,0,NULL, "can't open %s to write the tv",
299: outfilnm);
300: #if AR16WR
301: tvbuf = myalloc( BUFSIZ );
302: setbuf( tvstream, tvbuf );
303: #endif
304:
305: fseek(tvstream, tvspec.tvosptr->oshdr.s_scnptr, 0);
306: for ( indx = tvspec.tvrange[0]; indx <= tvspec.tvrange[1]; ++indx)
307: fwrite(&tvspec.tvfill, TVENTSZ, 1, tvstream);
308:
309: /*
310: * For each tv slot assignment directive, write out that slot
311: * if it is legal and update the ld symbol table appropriately.
312: *
313: * TV slot assignment directives are of the form "func=n" where
314: * func is a function name and n is a transfer vector index. Another
315: * directive specifies a RANGE for the current load, meaning that
316: * all functions defined in this load are to be assigned slots within
317: * the given range. Slot assignment directives for functions outside
318: * the given range are for use in resolving references to functions
319: * defined in another subsystem. Ld depends on the veracity and
320: * competence of the user to insure that this is, after all, the
321: * slot index assigned to this function in another subsystem load.
322: *
323: * Certain conditions result in errors.
324: * 1) If the user specifies a LENGTH for the total transfer vector,
325: * a) if more slots are required than the given LENGTH allows,
326: * or b) if an ASSIGN directive is issued which lies beyond the
327: * specified LENGTH
328: * then ERROR
329: * 2) If the user ASSIGNs a slot S to a function which is undefined
330: * in the current load, then
331: * if R1 <= S <= R2
332: * then ERROR
333: * {where R1 is n in the directive RANGE(n,m) or 0;
334: * R2 is m in the directive RANGE(n,m) or LENGTH; }
335: * 3) If the user ASSIGNs a slot to a function which is defined
336: * in the current load, then
337: * if NOT R1 <= S <= R2
338: * then ERROR
339: *
340: * Otherwise the assigned function slot is either used to update
341: * the ld symbol table and/or written to the a.out file or, if the
342: * assigned function is unreferenced in the current load, ignored.
343: */
344:
345: #if ONEPROC
346: while (tvslot1) {
347: #else
348: tvslot1 = (TVASSIGN *) myalloc(sizeof(TVASSIGN));
349:
350: while( tvslotcnt-- ) {
351: fread( tvslot1, sizeof(TVASSIGN), 1, trnfdes);
352: #endif
353:
354: /*
355: * If the symbol has never been referenced, ignore
356: * the ASSIGN command
357: */
358: if( (pglob = findsym(tvslot1->funname)) == NULL ) {
359: #if ONEPROC
360: tvslot1 = tvslot1->nxtslot;
361: #endif
362: continue;
363: }
364:
365: changed = 0;
366: if( pglob->sment.n_scnum == 0 ) {
367: /*
368: * The symbol is referenced but not defined
369: */
370: if( (tvslot1->slot >= tvspec.tvrange[0]) &&
371: (tvslot1->slot <= tvspec.tvrange[1]) ) {
372: PUTSYM(pglob, 0);
373: lderror(2,0,tvspec.tvinflnm,
374: "Undefined symbol assigned a tv slot within tv range");
375: }
376: else {
377: if( pglob->sment.n_numaux == 0 ) {
378: zero(&aux, AUXESZ);
379: paux = makeaux(pglob, &aux, 0);
380: changed = 1;
381: } else {
382: paux = getaux(pglob->smauxid);
383: }
384: paux->axent.x_sym.x_tvndx = P_TV;
385: PUTAUX(paux, 1);
386: }
387: }
388: else {
389: /*
390: * The symbol is defined
391: */
392: if( (tvslot1->slot < tvspec.tvrange[0]) ||
393: (tvslot1->slot > tvspec.tvrange[1]) ) {
394: PUTSYM(pglob, 0);
395: lderror(2,0,tvspec.tvinflnm,
396: "Defined symbol assigned a tv slot outside tv range");
397: }
398: }
399:
400: /*
401: * Update ld symbol table aux entry
402: */
403:
404: if (pglob->sment.n_numaux <= 0) {
405: if (pglob->sment.n_type == 0) {
406: zero( &aux, AUXESZ );
407: paux = makeaux(pglob, &aux, 0);
408: changed = 1;
409: paux->axent.x_sym.x_tvndx = N_TV;
410: } else
411: lderror(2,0,tvspec.tvinflnm,
412: "attempt to assign tv slot to illegal symbol");
413: } else
414: paux = getaux(pglob->smauxid);
415: /*
416: * If a function has been defined but never referenced
417: * through a tv, then the tvndx is still N_TV, not P_TV
418: * thus we need to test for both as the user may be
419: * defining the function for use in another subsystem
420: * and this code would otherwise generate an error message
421: */
422: if( paux->axent.x_sym.x_tvndx != P_TV && paux->axent.x_sym.x_tvndx != N_TV ) {
423: PUTAUX(paux,0);
424: PUTSYM(pglob, changed);
425: lderror(2,0,tvspec.tvinflnm,
426: "Two tv slot assignments for function %.8s: %d and %d",
427: SYMNAME( pglob->sment ),
428: paux->axent.x_sym.x_tvndx,
429: tvslot1->slot);
430: } else {
431: paux->axent.x_sym.x_tvndx = tvslot1->slot;
432: PUTAUX(paux, 1);
433: }
434:
435: /*
436: * Determine if it is necessary to actually write the
437: * tv slot to the output file: does the slot fall
438: * within the specified tv range
439: */
440:
441: if( (tvslot1->slot < tvspec.tvrange[0]) ||
442: (tvslot1->slot > tvspec.tvrange[1]) ) {
443: PUTSYM(pglob, changed);
444: #if ONEPROC
445: tvslot1 = tvslot1->nxtslot;
446: #endif
447: continue;
448: }
449:
450: offset = (long) (tvspec.tvosptr->oshdr.s_scnptr + (TVENTSZ *
451: (tvslot1->slot - tvspec.tvrange[0])));
452: fseek(tvstream, offset-ftell(tvstream), 1);
453:
454: setslot(pglob, psect, &outslot);
455:
456: PUTSYM(pglob, changed);
457: fwrite(&outslot, TVENTSZ, 1, tvstream);
458: #if ONEPROC
459: tvslot1 = tvslot1->nxtslot;
460: #endif
461: }
462:
463: fflush(tvstream);
464:
465: /*
466: * Don't use slot 0 as it is illegal in C to have any
467: * type of valid pointer with a zero value
468: */
469:
470: tvnxt = (tvspec.tvrange[0] != 0) ? tvspec.tvrange[0] : 1;
471:
472: /*
473: * Position tv file pointers:
474: * tvstream : start of the tv (range)
475: * tvread : start of first legal tv slot
476: */
477: if( (tvread=fopen(outfilnm,"r")) == NULL )
478: lderror(2,0,NULL,"Cannot open %s to read tv",outfilnm);
479: fseek(tvstream, tvspec.tvosptr->oshdr.s_scnptr, 0);
480: fseek(tvread, ((long)(tvspec.tvosptr->oshdr.s_scnptr +
481: (tvspec.tvrange[0] ? 0 : TVENTSZ))), 0);
482:
483: return;
484:
485: #endif
486: }
487: /*eject*/
488: tvslot(pglob, psym, infile, cs)
489: SYMTAB *pglob; /* symbol is in ld symbol table entry */
490: struct syment *psym; /* symtab entry is from input file */
491: FILE *infile; /* input file stream */
492: unsigned short cs; /* segment value; used only for psym */
493: {
494:
495: /*
496: * Check whether the given symbol needs a slot
497: * in the tv. If it does and one has not be assigned,
498: * assign the symbol a slot and write that entry to the
499: * tv in the output file
500: *
501: * The symbol is passed in either a gsym or syment symbol table
502: * entry data structure. For a gsym, look in the aux entry; for a
503: * syment, read the next syment from the input file.
504: * In the latter case, reseek the input file pointer to the original
505: * position
506: *
507: * The return value is:
508: *
509: * -1 symbol doesn't need tv slot
510: * 0 symbol needs tv slot, but this is
511: * not an absolute load
512: * >0 tv slot needed and assigned
513: */
514:
515: int ret;
516: long offset; /* where to write next */
517: AUXTAB *paux;
518: TVENTRY tvent;
519: AUXENT aux;
520:
521: if( ! tvflag )
522: return ( -1 );
523:
524: #if TRVEC
525: if( pglob != NULL )
526: psym = &pglob->sment;
527:
528: /*
529: * We are assured that all symbols needing tv slots have already
530: * been detected in loadobjf() and their tvndx set to P_TV.
531: * Except for static functions which are detected in psymtab
532: * but their tvndx left at N_TV
533: */
534:
535: if( psym->n_numaux == 0 )
536: return ( -1 );
537:
538: if( pglob != NULL ) {
539: /*
540: * This routine was given an ld symbol table entry
541: */
542: paux = getaux(pglob->smauxid);
543: if (paux->axent.x_sym.x_tvndx == 0 ) {
544: PUTAUX(paux, 0);
545: return ( -1 );
546: }
547: if( paux->axent.x_sym.x_tvndx == P_TV ) {
548: /*
549: * This symbol is referenced through a tv:
550: *
551: * tvndx == P_TV
552: */
553: if( ! aflag ) {
554: PUTAUX(paux, 0);
555: return ( 0 );
556: }
557: if( pglob->smscnptr != NULL ) {
558:
559: slotassign(&tvent, pglob, &cs);
560:
561: }
562: else {
563: /*
564: * tv ref to undefined symbol
565: */
566: PUTAUX(paux, 0);
567: return( -1 );
568: }
569: if ( (offset = gettvnxt()) == 0L)
570: lderror(2,0,NULL,"Internal error: out of tv slots");
571: fseek(tvstream, offset-ftell(tvstream), 1);
572: paux->axent.x_sym.x_tvndx = tvnxt;
573: PUTAUX(paux, 1);
574: }
575: else {
576: if( paux->axent.x_sym.x_tvndx == N_TV )
577: ret = 0;
578: else
579: /*
580: * The symbol already has a tv assigned:
581: *
582: * tvndx > 0
583: */
584: ret = paux->axent.x_sym.x_tvndx;
585: PUTAUX(paux, 0);
586: return ( ret );
587: }
588: }
589: else {
590: /*
591: * This routine was given an input symbol table entry
592: */
593: fread(&aux, AUXESZ, 1, infile);
594: fseek(infile, (long) -AUXESZ, 1);
595: if( aux.x_sym.x_tvndx != N_TV )
596: return ( -1 );
597: if( ! aflag )
598: return ( 0 );
599: if ( (offset = gettvnxt()) == 0L)
600: lderror(2,0,NULL,"Internal error: out of tv slots");
601: fseek(tvstream, offset-ftell(tvstream), 1);
602:
603: settventry(&tvent, psym, &cs);
604: }
605:
606: fwrite(&tvent, TVENTSZ, 1, tvstream);
607:
608: return ( tvnxt++ );
609:
610: #endif
611: }
612: /*eject*/
613: long
614: gettvnxt()
615: {
616: #if TRVEC
617:
618: /*
619: * This routine is called only when it is certain that we need to write
620: * out a tv entry. tvinit() has written assigned slots and filled all
621: * empty slots with 0. The first time this routine is called tvnxt equals
622: * tvspec.tvrange[0], tvread is open for reading, the read/write
623: * pointer is at the beginning of the transfer vector.
624: *
625: * It begins reading transfer vector entries until it finds
626: * one which is unused. It then seeks TVENTSZ backwards and returns
627: * the file offset of the available slot. It increments tvnxt whenever
628: * it finds an unavailable slot. When next called, it will be in a position
629: * to proceed reading slots, being positioned correctly and knowing
630: * the value of tvnxt.
631: */
632:
633: long inslot;
634:
635: for( ; tvnxt <= tvspec.tvrange[1]; ++tvnxt ) {
636: fread(&inslot, TVENTSZ, 1, tvread);
637: if( inslot == -1L )
638: return( (long)(ftell(tvread)-TVENTSZ) );
639: }
640:
641: return(0);
642: #endif
643: }
644: /*eject*/
645: tvfinish()
646: {
647: #if TRVEC
648: long offset;
649: SYMTAB *psym;
650:
651: if( tvspec.tvfnfill[0] != '\0' ) {
652: if( (psym = findsym(tvspec.tvfnfill)) == NULL )
653: lderror(1, tvspec.tvinlnno, tvspec.tvinflnm,
654: "tv fill symbol %s does not exist", tvspec.tvfnfill);
655: else {
656:
657: filltvassign(psym);
658:
659: PUTSYM(psym, 0);
660:
661: /*
662: * update x_tvfill in .tv aux entry
663: */
664: if ( (psym=findsym(_TV)) == NULL)
665: lderror(0,0,NULL,"No .tv in symbol table");
666: else {
667: AUXTAB *paux;
668: union {
669: TVENTRY ent;
670: long l;
671: } fill;
672:
673: fill.ent = tvspec.tvfill;
674: paux = getaux(psym->smauxid);
675: paux->axent.x_tv.x_tvfill = fill.l;
676: PUTAUX(paux, 1);
677: PUTSYM(psym, 0);
678: }
679:
680: if ( tvspec.tvrange[0] == 0 ) { /* fill slot 0 */
681: fseek(tvstream, tvspec.tvosptr->oshdr.s_scnptr, 0);
682: fwrite(&tvspec.tvfill, TVENTSZ, 1, tvstream);
683: }
684:
685: for( offset = gettvnxt(); tvnxt <= tvspec.tvrange[1]; ) {
686: fseek(tvstream, offset-ftell(tvstream), 1);
687: fwrite(&tvspec.tvfill, TVENTSZ, 1, tvstream);
688: ++tvnxt;
689: offset = gettvnxt();
690: } /* for */
691: } /* else */
692: } /* if tvfnfill[0] */
693:
694: fclose(tvstream);
695: #if AR16WR
696: free( tvbuf );
697: #endif
698: fclose(tvread);
699:
700: #endif
701: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.