xref: /dragonfly/contrib/less/ch.c (revision 320d7c8a)
11133e27eSPeter Avalos /*
2*320d7c8aSAaron LI  * Copyright (C) 1984-2023  Mark Nudelman
31133e27eSPeter Avalos  *
41133e27eSPeter Avalos  * You may distribute under the terms of either the GNU General Public
51133e27eSPeter Avalos  * License or the Less License, as specified in the README file.
61133e27eSPeter Avalos  *
7e639dc31SJohn Marino  * For more information, see the README file.
81133e27eSPeter Avalos  */
91133e27eSPeter Avalos 
101133e27eSPeter Avalos 
111133e27eSPeter Avalos /*
121133e27eSPeter Avalos  * Low level character input from the input file.
131133e27eSPeter Avalos  * We use these special purpose routines which optimize moving
141133e27eSPeter Avalos  * both forward and backward from the current read pointer.
151133e27eSPeter Avalos  */
161133e27eSPeter Avalos 
171133e27eSPeter Avalos #include "less.h"
181133e27eSPeter Avalos #if MSDOS_COMPILER==WIN32C
191133e27eSPeter Avalos #include <errno.h>
201133e27eSPeter Avalos #include <windows.h>
211133e27eSPeter Avalos #endif
221133e27eSPeter Avalos 
230c7ad07eSAntonio Huete Jimenez #if HAVE_PROCFS
240c7ad07eSAntonio Huete Jimenez #include <sys/statfs.h>
25*320d7c8aSAaron LI #if HAVE_LINUX_MAGIC_H
26*320d7c8aSAaron LI #include <linux/magic.h>
27*320d7c8aSAaron LI #endif
280c7ad07eSAntonio Huete Jimenez #endif
290c7ad07eSAntonio Huete Jimenez 
301133e27eSPeter Avalos typedef POSITION BLOCKNUM;
311133e27eSPeter Avalos 
321133e27eSPeter Avalos public int ignore_eoi;
331133e27eSPeter Avalos 
341133e27eSPeter Avalos /*
351133e27eSPeter Avalos  * Pool of buffers holding the most recently used blocks of the input file.
361133e27eSPeter Avalos  * The buffer pool is kept as a doubly-linked circular list,
371133e27eSPeter Avalos  * in order from most- to least-recently used.
381133e27eSPeter Avalos  * The circular list is anchored by the file state "thisfile".
391133e27eSPeter Avalos  */
408be36e5bSPeter Avalos struct bufnode {
418be36e5bSPeter Avalos 	struct bufnode *next, *prev;
428be36e5bSPeter Avalos 	struct bufnode *hnext, *hprev;
438be36e5bSPeter Avalos };
448be36e5bSPeter Avalos 
451133e27eSPeter Avalos #define LBUFSIZE        8192
461133e27eSPeter Avalos struct buf {
478be36e5bSPeter Avalos 	struct bufnode node;
481133e27eSPeter Avalos 	BLOCKNUM block;
491133e27eSPeter Avalos 	unsigned int datasize;
501133e27eSPeter Avalos 	unsigned char data[LBUFSIZE];
511133e27eSPeter Avalos };
528be36e5bSPeter Avalos #define bufnode_buf(bn)  ((struct buf *) bn)
531133e27eSPeter Avalos 
541133e27eSPeter Avalos /*
551133e27eSPeter Avalos  * The file state is maintained in a filestate structure.
561133e27eSPeter Avalos  * A pointer to the filestate is kept in the ifile structure.
571133e27eSPeter Avalos  */
58fa0be7c5SJohn Marino #define BUFHASH_SIZE    1024
591133e27eSPeter Avalos struct filestate {
608be36e5bSPeter Avalos 	struct bufnode buflist;
618be36e5bSPeter Avalos 	struct bufnode hashtbl[BUFHASH_SIZE];
621133e27eSPeter Avalos 	int file;
631133e27eSPeter Avalos 	int flags;
641133e27eSPeter Avalos 	POSITION fpos;
651133e27eSPeter Avalos 	int nbufs;
661133e27eSPeter Avalos 	BLOCKNUM block;
671133e27eSPeter Avalos 	unsigned int offset;
681133e27eSPeter Avalos 	POSITION fsize;
691133e27eSPeter Avalos };
701133e27eSPeter Avalos 
718be36e5bSPeter Avalos #define ch_bufhead      thisfile->buflist.next
728be36e5bSPeter Avalos #define ch_buftail      thisfile->buflist.prev
731133e27eSPeter Avalos #define ch_nbufs        thisfile->nbufs
741133e27eSPeter Avalos #define ch_block        thisfile->block
751133e27eSPeter Avalos #define ch_offset       thisfile->offset
761133e27eSPeter Avalos #define ch_fpos         thisfile->fpos
771133e27eSPeter Avalos #define ch_fsize        thisfile->fsize
781133e27eSPeter Avalos #define ch_flags        thisfile->flags
791133e27eSPeter Avalos #define ch_file         thisfile->file
801133e27eSPeter Avalos 
818be36e5bSPeter Avalos #define END_OF_CHAIN    (&thisfile->buflist)
828be36e5bSPeter Avalos #define END_OF_HCHAIN(h) (&thisfile->hashtbl[h])
831133e27eSPeter Avalos #define BUFHASH(blk)    ((blk) & (BUFHASH_SIZE-1))
841133e27eSPeter Avalos 
858be36e5bSPeter Avalos /*
868be36e5bSPeter Avalos  * Macros to manipulate the list of buffers in thisfile->buflist.
878be36e5bSPeter Avalos  */
888be36e5bSPeter Avalos #define FOR_BUFS(bn) \
898be36e5bSPeter Avalos 	for (bn = ch_bufhead;  bn != END_OF_CHAIN;  bn = bn->next)
901133e27eSPeter Avalos 
918be36e5bSPeter Avalos #define BUF_RM(bn) \
928be36e5bSPeter Avalos 	(bn)->next->prev = (bn)->prev; \
938be36e5bSPeter Avalos 	(bn)->prev->next = (bn)->next;
941133e27eSPeter Avalos 
958be36e5bSPeter Avalos #define BUF_INS_HEAD(bn) \
968be36e5bSPeter Avalos 	(bn)->next = ch_bufhead; \
978be36e5bSPeter Avalos 	(bn)->prev = END_OF_CHAIN; \
988be36e5bSPeter Avalos 	ch_bufhead->prev = (bn); \
998be36e5bSPeter Avalos 	ch_bufhead = (bn);
1008be36e5bSPeter Avalos 
1018be36e5bSPeter Avalos #define BUF_INS_TAIL(bn) \
1028be36e5bSPeter Avalos 	(bn)->next = END_OF_CHAIN; \
1038be36e5bSPeter Avalos 	(bn)->prev = ch_buftail; \
1048be36e5bSPeter Avalos 	ch_buftail->next = (bn); \
1058be36e5bSPeter Avalos 	ch_buftail = (bn);
1068be36e5bSPeter Avalos 
1078be36e5bSPeter Avalos /*
1088be36e5bSPeter Avalos  * Macros to manipulate the list of buffers in thisfile->hashtbl[n].
1098be36e5bSPeter Avalos  */
1108be36e5bSPeter Avalos #define FOR_BUFS_IN_CHAIN(h,bn) \
1118be36e5bSPeter Avalos 	for (bn = thisfile->hashtbl[h].hnext;  \
1128be36e5bSPeter Avalos 	     bn != END_OF_HCHAIN(h);  bn = bn->hnext)
1138be36e5bSPeter Avalos 
1148be36e5bSPeter Avalos #define BUF_HASH_RM(bn) \
1158be36e5bSPeter Avalos 	(bn)->hnext->hprev = (bn)->hprev; \
1168be36e5bSPeter Avalos 	(bn)->hprev->hnext = (bn)->hnext;
1178be36e5bSPeter Avalos 
1188be36e5bSPeter Avalos #define BUF_HASH_INS(bn,h) \
1198be36e5bSPeter Avalos 	(bn)->hnext = thisfile->hashtbl[h].hnext; \
1208be36e5bSPeter Avalos 	(bn)->hprev = END_OF_HCHAIN(h); \
1218be36e5bSPeter Avalos 	thisfile->hashtbl[h].hnext->hprev = (bn); \
1228be36e5bSPeter Avalos 	thisfile->hashtbl[h].hnext = (bn);
1231133e27eSPeter Avalos 
1241133e27eSPeter Avalos static struct filestate *thisfile;
1251133e27eSPeter Avalos static int ch_ungotchar = -1;
1261133e27eSPeter Avalos static int maxbufs = -1;
1271133e27eSPeter Avalos 
1281133e27eSPeter Avalos extern int autobuf;
1291133e27eSPeter Avalos extern int sigs;
1301133e27eSPeter Avalos extern int secure;
1311133e27eSPeter Avalos extern int screen_trashed;
1321133e27eSPeter Avalos extern int follow_mode;
133*320d7c8aSAaron LI extern int waiting_for_data;
1341133e27eSPeter Avalos extern constant char helpdata[];
1351133e27eSPeter Avalos extern constant int size_helpdata;
1361133e27eSPeter Avalos extern IFILE curr_ifile;
1371133e27eSPeter Avalos #if LOGFILE
1381133e27eSPeter Avalos extern int logfile;
1391133e27eSPeter Avalos extern char *namelogfile;
1401133e27eSPeter Avalos #endif
1411133e27eSPeter Avalos 
1421133e27eSPeter Avalos static int ch_addbuf();
1431133e27eSPeter Avalos 
1441133e27eSPeter Avalos 
1451133e27eSPeter Avalos /*
1461133e27eSPeter Avalos  * Get the character pointed to by the read pointer.
1471133e27eSPeter Avalos  */
ch_get(void)148*320d7c8aSAaron LI static int ch_get(void)
1491133e27eSPeter Avalos {
15002d62a0fSDaniel Fojt 	struct buf *bp;
15102d62a0fSDaniel Fojt 	struct bufnode *bn;
15202d62a0fSDaniel Fojt 	int n;
153*320d7c8aSAaron LI 	int read_again;
15402d62a0fSDaniel Fojt 	int h;
1551133e27eSPeter Avalos 	POSITION pos;
1561133e27eSPeter Avalos 	POSITION len;
1571133e27eSPeter Avalos 
1581133e27eSPeter Avalos 	if (thisfile == NULL)
1591133e27eSPeter Avalos 		return (EOI);
1601133e27eSPeter Avalos 
1618be36e5bSPeter Avalos 	/*
1628be36e5bSPeter Avalos 	 * Quick check for the common case where
1638be36e5bSPeter Avalos 	 * the desired char is in the head buffer.
1648be36e5bSPeter Avalos 	 */
1658be36e5bSPeter Avalos 	if (ch_bufhead != END_OF_CHAIN)
1668be36e5bSPeter Avalos 	{
1678be36e5bSPeter Avalos 		bp = bufnode_buf(ch_bufhead);
1688be36e5bSPeter Avalos 		if (ch_block == bp->block && ch_offset < bp->datasize)
1698be36e5bSPeter Avalos 			return bp->data[ch_offset];
1708be36e5bSPeter Avalos 	}
1718be36e5bSPeter Avalos 
1721133e27eSPeter Avalos 	/*
1731133e27eSPeter Avalos 	 * Look for a buffer holding the desired block.
1741133e27eSPeter Avalos 	 */
175*320d7c8aSAaron LI 	waiting_for_data = FALSE;
1761133e27eSPeter Avalos 	h = BUFHASH(ch_block);
1778be36e5bSPeter Avalos 	FOR_BUFS_IN_CHAIN(h, bn)
1781133e27eSPeter Avalos 	{
1798be36e5bSPeter Avalos 		bp = bufnode_buf(bn);
1801133e27eSPeter Avalos 		if (bp->block == ch_block)
1811133e27eSPeter Avalos 		{
1821133e27eSPeter Avalos 			if (ch_offset >= bp->datasize)
1831133e27eSPeter Avalos 				/*
1841133e27eSPeter Avalos 				 * Need more data in this buffer.
1851133e27eSPeter Avalos 				 */
1868be36e5bSPeter Avalos 				break;
1871133e27eSPeter Avalos 			goto found;
1881133e27eSPeter Avalos 		}
1891133e27eSPeter Avalos 	}
1908be36e5bSPeter Avalos 	if (bn == END_OF_HCHAIN(h))
1918be36e5bSPeter Avalos 	{
1921133e27eSPeter Avalos 		/*
1931133e27eSPeter Avalos 		 * Block is not in a buffer.
1941133e27eSPeter Avalos 		 * Take the least recently used buffer
1951133e27eSPeter Avalos 		 * and read the desired block into it.
1961133e27eSPeter Avalos 		 * If the LRU buffer has data in it,
1971133e27eSPeter Avalos 		 * then maybe allocate a new buffer.
1981133e27eSPeter Avalos 		 */
1998be36e5bSPeter Avalos 		if (ch_buftail == END_OF_CHAIN ||
2008be36e5bSPeter Avalos 			bufnode_buf(ch_buftail)->block != -1)
2011133e27eSPeter Avalos 		{
2021133e27eSPeter Avalos 			/*
2031133e27eSPeter Avalos 			 * There is no empty buffer to use.
2041133e27eSPeter Avalos 			 * Allocate a new buffer if:
2051133e27eSPeter Avalos 			 * 1. We can't seek on this file and -b is not in effect; or
2061133e27eSPeter Avalos 			 * 2. We haven't allocated the max buffers for this file yet.
2071133e27eSPeter Avalos 			 */
2081133e27eSPeter Avalos 			if ((autobuf && !(ch_flags & CH_CANSEEK)) ||
2091133e27eSPeter Avalos 				(maxbufs < 0 || ch_nbufs < maxbufs))
2101133e27eSPeter Avalos 				if (ch_addbuf())
2111133e27eSPeter Avalos 					/*
2121133e27eSPeter Avalos 					 * Allocation failed: turn off autobuf.
2131133e27eSPeter Avalos 					 */
2141133e27eSPeter Avalos 					autobuf = OPT_OFF;
2151133e27eSPeter Avalos 		}
2168be36e5bSPeter Avalos 		bn = ch_buftail;
2178be36e5bSPeter Avalos 		bp = bufnode_buf(bn);
2188be36e5bSPeter Avalos 		BUF_HASH_RM(bn); /* Remove from old hash chain. */
2191133e27eSPeter Avalos 		bp->block = ch_block;
2201133e27eSPeter Avalos 		bp->datasize = 0;
2218be36e5bSPeter Avalos 		BUF_HASH_INS(bn, h); /* Insert into new hash chain. */
2228be36e5bSPeter Avalos 	}
2231133e27eSPeter Avalos 
224*320d7c8aSAaron LI 	for (;;)
225*320d7c8aSAaron LI 	{
2261133e27eSPeter Avalos 		pos = (ch_block * LBUFSIZE) + bp->datasize;
2271133e27eSPeter Avalos 		if ((len = ch_length()) != NULL_POSITION && pos >= len)
2281133e27eSPeter Avalos 			/*
2291133e27eSPeter Avalos 			 * At end of file.
2301133e27eSPeter Avalos 			 */
2311133e27eSPeter Avalos 			return (EOI);
2321133e27eSPeter Avalos 
2331133e27eSPeter Avalos 		if (pos != ch_fpos)
2341133e27eSPeter Avalos 		{
2351133e27eSPeter Avalos 			/*
2361133e27eSPeter Avalos 			 * Not at the correct position: must seek.
2371133e27eSPeter Avalos 			 * If input is a pipe, we're in trouble (can't seek on a pipe).
2381133e27eSPeter Avalos 			 * Some data has been lost: just return "?".
2391133e27eSPeter Avalos 			 */
2401133e27eSPeter Avalos 			if (!(ch_flags & CH_CANSEEK))
2411133e27eSPeter Avalos 				return ('?');
2421133e27eSPeter Avalos 			if (lseek(ch_file, (off_t)pos, SEEK_SET) == BAD_LSEEK)
2431133e27eSPeter Avalos 			{
2441133e27eSPeter Avalos 				error("seek error", NULL_PARG);
2451133e27eSPeter Avalos 				clear_eol();
2461133e27eSPeter Avalos 				return (EOI);
2471133e27eSPeter Avalos 			}
2481133e27eSPeter Avalos 			ch_fpos = pos;
2491133e27eSPeter Avalos 		}
2501133e27eSPeter Avalos 
2511133e27eSPeter Avalos 		/*
2521133e27eSPeter Avalos 		 * Read the block.
2531133e27eSPeter Avalos 		 * If we read less than a full block, that's ok.
2541133e27eSPeter Avalos 		 * We use partial block and pick up the rest next time.
2551133e27eSPeter Avalos 		 */
2561133e27eSPeter Avalos 		if (ch_ungotchar != -1)
2571133e27eSPeter Avalos 		{
2581133e27eSPeter Avalos 			bp->data[bp->datasize] = ch_ungotchar;
2591133e27eSPeter Avalos 			n = 1;
2601133e27eSPeter Avalos 			ch_ungotchar = -1;
2611133e27eSPeter Avalos 		} else if (ch_flags & CH_HELPFILE)
2621133e27eSPeter Avalos 		{
2631133e27eSPeter Avalos 			bp->data[bp->datasize] = helpdata[ch_fpos];
2641133e27eSPeter Avalos 			n = 1;
2651133e27eSPeter Avalos 		} else
2661133e27eSPeter Avalos 		{
2671133e27eSPeter Avalos 			n = iread(ch_file, &bp->data[bp->datasize],
2681133e27eSPeter Avalos 				(unsigned int)(LBUFSIZE - bp->datasize));
2691133e27eSPeter Avalos 		}
2701133e27eSPeter Avalos 
271*320d7c8aSAaron LI 		read_again = FALSE;
2721133e27eSPeter Avalos 		if (n == READ_INTR)
273*320d7c8aSAaron LI 		{
274*320d7c8aSAaron LI 			ch_fsize = pos;
2751133e27eSPeter Avalos 			return (EOI);
276*320d7c8aSAaron LI 		}
277*320d7c8aSAaron LI 		if (n == READ_AGAIN)
278*320d7c8aSAaron LI 		{
279*320d7c8aSAaron LI 			read_again = TRUE;
280*320d7c8aSAaron LI 			n = 0;
281*320d7c8aSAaron LI 		}
2821133e27eSPeter Avalos 		if (n < 0)
2831133e27eSPeter Avalos 		{
2841133e27eSPeter Avalos #if MSDOS_COMPILER==WIN32C
2851133e27eSPeter Avalos 			if (errno != EPIPE)
2861133e27eSPeter Avalos #endif
2871133e27eSPeter Avalos 			{
2881133e27eSPeter Avalos 				error("read error", NULL_PARG);
2891133e27eSPeter Avalos 				clear_eol();
2901133e27eSPeter Avalos 			}
2911133e27eSPeter Avalos 			n = 0;
2921133e27eSPeter Avalos 		}
2931133e27eSPeter Avalos 
2941133e27eSPeter Avalos #if LOGFILE
2951133e27eSPeter Avalos 		/*
2961133e27eSPeter Avalos 		 * If we have a log file, write the new data to it.
2971133e27eSPeter Avalos 		 */
2981133e27eSPeter Avalos 		if (!secure && logfile >= 0 && n > 0)
2991133e27eSPeter Avalos 			write(logfile, (char *) &bp->data[bp->datasize], n);
3001133e27eSPeter Avalos #endif
3011133e27eSPeter Avalos 
3021133e27eSPeter Avalos 		ch_fpos += n;
3031133e27eSPeter Avalos 		bp->datasize += n;
3041133e27eSPeter Avalos 
3051133e27eSPeter Avalos 		if (n == 0)
3061133e27eSPeter Avalos 		{
307*320d7c8aSAaron LI 			/* Either end of file or no data available.
308*320d7c8aSAaron LI 			 * read_again indicates the latter. */
309*320d7c8aSAaron LI 			if (!read_again)
3101133e27eSPeter Avalos 				ch_fsize = pos;
311*320d7c8aSAaron LI 			if (ignore_eoi || read_again)
3121133e27eSPeter Avalos 			{
313*320d7c8aSAaron LI 				/* Wait a while, then try again. */
314*320d7c8aSAaron LI 				if (!waiting_for_data)
3151133e27eSPeter Avalos 				{
3161133e27eSPeter Avalos 					PARG parg;
3171133e27eSPeter Avalos 					parg.p_string = wait_message();
318*320d7c8aSAaron LI 					ixerror("%s", &parg);
319*320d7c8aSAaron LI 					waiting_for_data = TRUE;
3201133e27eSPeter Avalos 				}
321*320d7c8aSAaron LI 				sleep_ms(50); /* Reduce system load */
322*320d7c8aSAaron LI 			}
323*320d7c8aSAaron LI 			if (ignore_eoi && follow_mode == FOLLOW_NAME && curr_ifile_changed())
3241133e27eSPeter Avalos 			{
325*320d7c8aSAaron LI 				/* screen_trashed=2 causes make_display to reopen the file. */
3261133e27eSPeter Avalos 				screen_trashed = 2;
3271133e27eSPeter Avalos 				return (EOI);
3281133e27eSPeter Avalos 			}
3291133e27eSPeter Avalos 			if (sigs)
3301133e27eSPeter Avalos 				return (EOI);
3311133e27eSPeter Avalos 		}
3321133e27eSPeter Avalos 
3331133e27eSPeter Avalos 		found:
3348be36e5bSPeter Avalos 		if (ch_bufhead != bn)
3351133e27eSPeter Avalos 		{
3361133e27eSPeter Avalos 			/*
3371133e27eSPeter Avalos 			 * Move the buffer to the head of the buffer chain.
3381133e27eSPeter Avalos 			 * This orders the buffer chain, most- to least-recently used.
3391133e27eSPeter Avalos 			 */
3408be36e5bSPeter Avalos 			BUF_RM(bn);
3418be36e5bSPeter Avalos 			BUF_INS_HEAD(bn);
3421133e27eSPeter Avalos 
3431133e27eSPeter Avalos 			/*
3441133e27eSPeter Avalos 			 * Move to head of hash chain too.
3451133e27eSPeter Avalos 			 */
3468be36e5bSPeter Avalos 			BUF_HASH_RM(bn);
3478be36e5bSPeter Avalos 			BUF_HASH_INS(bn, h);
3481133e27eSPeter Avalos 		}
3491133e27eSPeter Avalos 
350*320d7c8aSAaron LI 		if (ch_offset < bp->datasize)
351*320d7c8aSAaron LI 			break;
3521133e27eSPeter Avalos 		/*
3531133e27eSPeter Avalos 		 * After all that, we still don't have enough data.
3541133e27eSPeter Avalos 		 * Go back and try again.
3551133e27eSPeter Avalos 		 */
356*320d7c8aSAaron LI 	}
3571133e27eSPeter Avalos 	return (bp->data[ch_offset]);
3581133e27eSPeter Avalos }
3591133e27eSPeter Avalos 
3601133e27eSPeter Avalos /*
3611133e27eSPeter Avalos  * ch_ungetchar is a rather kludgy and limited way to push
3621133e27eSPeter Avalos  * a single char onto an input file descriptor.
3631133e27eSPeter Avalos  */
ch_ungetchar(int c)364*320d7c8aSAaron LI public void ch_ungetchar(int c)
3651133e27eSPeter Avalos {
3661133e27eSPeter Avalos 	if (c != -1 && ch_ungotchar != -1)
3671133e27eSPeter Avalos 		error("ch_ungetchar overrun", NULL_PARG);
3681133e27eSPeter Avalos 	ch_ungotchar = c;
3691133e27eSPeter Avalos }
3701133e27eSPeter Avalos 
3711133e27eSPeter Avalos #if LOGFILE
3721133e27eSPeter Avalos /*
3731133e27eSPeter Avalos  * Close the logfile.
3741133e27eSPeter Avalos  * If we haven't read all of standard input into it, do that now.
3751133e27eSPeter Avalos  */
end_logfile(void)376*320d7c8aSAaron LI public void end_logfile(void)
3771133e27eSPeter Avalos {
3781133e27eSPeter Avalos 	static int tried = FALSE;
3791133e27eSPeter Avalos 
3801133e27eSPeter Avalos 	if (logfile < 0)
3811133e27eSPeter Avalos 		return;
3821133e27eSPeter Avalos 	if (!tried && ch_fsize == NULL_POSITION)
3831133e27eSPeter Avalos 	{
3841133e27eSPeter Avalos 		tried = TRUE;
3851133e27eSPeter Avalos 		ierror("Finishing logfile", NULL_PARG);
3861133e27eSPeter Avalos 		while (ch_forw_get() != EOI)
3871133e27eSPeter Avalos 			if (ABORT_SIGS())
3881133e27eSPeter Avalos 				break;
3891133e27eSPeter Avalos 	}
3901133e27eSPeter Avalos 	close(logfile);
3911133e27eSPeter Avalos 	logfile = -1;
3920c7ad07eSAntonio Huete Jimenez 	free(namelogfile);
3931133e27eSPeter Avalos 	namelogfile = NULL;
3941133e27eSPeter Avalos }
3951133e27eSPeter Avalos 
3961133e27eSPeter Avalos /*
3971133e27eSPeter Avalos  * Start a log file AFTER less has already been running.
3981133e27eSPeter Avalos  * Invoked from the - command; see toggle_option().
3991133e27eSPeter Avalos  * Write all the existing buffered data to the log file.
4001133e27eSPeter Avalos  */
sync_logfile(void)401*320d7c8aSAaron LI public void sync_logfile(void)
4021133e27eSPeter Avalos {
40302d62a0fSDaniel Fojt 	struct buf *bp;
40402d62a0fSDaniel Fojt 	struct bufnode *bn;
4051133e27eSPeter Avalos 	int warned = FALSE;
4061133e27eSPeter Avalos 	BLOCKNUM block;
4071133e27eSPeter Avalos 	BLOCKNUM nblocks;
4081133e27eSPeter Avalos 
409*320d7c8aSAaron LI 	if (logfile < 0)
410*320d7c8aSAaron LI 		return;
4111133e27eSPeter Avalos 	nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE;
4121133e27eSPeter Avalos 	for (block = 0;  block < nblocks;  block++)
4131133e27eSPeter Avalos 	{
4148be36e5bSPeter Avalos 		int wrote = FALSE;
4158be36e5bSPeter Avalos 		FOR_BUFS(bn)
4161133e27eSPeter Avalos 		{
4178be36e5bSPeter Avalos 			bp = bufnode_buf(bn);
4188be36e5bSPeter Avalos 			if (bp->block == block)
4191133e27eSPeter Avalos 			{
4208be36e5bSPeter Avalos 				write(logfile, (char *) bp->data, bp->datasize);
4218be36e5bSPeter Avalos 				wrote = TRUE;
4228be36e5bSPeter Avalos 				break;
4238be36e5bSPeter Avalos 			}
4248be36e5bSPeter Avalos 		}
4258be36e5bSPeter Avalos 		if (!wrote && !warned)
4261133e27eSPeter Avalos 		{
4271133e27eSPeter Avalos 			error("Warning: log file is incomplete",
4281133e27eSPeter Avalos 				NULL_PARG);
4291133e27eSPeter Avalos 			warned = TRUE;
4301133e27eSPeter Avalos 		}
4311133e27eSPeter Avalos 	}
4321133e27eSPeter Avalos }
4331133e27eSPeter Avalos 
4341133e27eSPeter Avalos #endif
4351133e27eSPeter Avalos 
4361133e27eSPeter Avalos /*
4371133e27eSPeter Avalos  * Determine if a specific block is currently in one of the buffers.
4381133e27eSPeter Avalos  */
buffered(BLOCKNUM block)439*320d7c8aSAaron LI static int buffered(BLOCKNUM block)
4401133e27eSPeter Avalos {
44102d62a0fSDaniel Fojt 	struct buf *bp;
44202d62a0fSDaniel Fojt 	struct bufnode *bn;
44302d62a0fSDaniel Fojt 	int h;
4441133e27eSPeter Avalos 
4451133e27eSPeter Avalos 	h = BUFHASH(block);
4468be36e5bSPeter Avalos 	FOR_BUFS_IN_CHAIN(h, bn)
4471133e27eSPeter Avalos 	{
4488be36e5bSPeter Avalos 		bp = bufnode_buf(bn);
4491133e27eSPeter Avalos 		if (bp->block == block)
4501133e27eSPeter Avalos 			return (TRUE);
4511133e27eSPeter Avalos 	}
4521133e27eSPeter Avalos 	return (FALSE);
4531133e27eSPeter Avalos }
4541133e27eSPeter Avalos 
4551133e27eSPeter Avalos /*
4561133e27eSPeter Avalos  * Seek to a specified position in the file.
4571133e27eSPeter Avalos  * Return 0 if successful, non-zero if can't seek there.
4581133e27eSPeter Avalos  */
ch_seek(POSITION pos)459*320d7c8aSAaron LI public int ch_seek(POSITION pos)
4601133e27eSPeter Avalos {
4611133e27eSPeter Avalos 	BLOCKNUM new_block;
4621133e27eSPeter Avalos 	POSITION len;
4631133e27eSPeter Avalos 
4641133e27eSPeter Avalos 	if (thisfile == NULL)
4651133e27eSPeter Avalos 		return (0);
4661133e27eSPeter Avalos 
4671133e27eSPeter Avalos 	len = ch_length();
4681133e27eSPeter Avalos 	if (pos < ch_zero() || (len != NULL_POSITION && pos > len))
4691133e27eSPeter Avalos 		return (1);
4701133e27eSPeter Avalos 
4711133e27eSPeter Avalos 	new_block = pos / LBUFSIZE;
4721133e27eSPeter Avalos 	if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && !buffered(new_block))
4731133e27eSPeter Avalos 	{
4741133e27eSPeter Avalos 		if (ch_fpos > pos)
4751133e27eSPeter Avalos 			return (1);
4761133e27eSPeter Avalos 		while (ch_fpos < pos)
4771133e27eSPeter Avalos 		{
4781133e27eSPeter Avalos 			if (ch_forw_get() == EOI)
4791133e27eSPeter Avalos 				return (1);
4801133e27eSPeter Avalos 			if (ABORT_SIGS())
4811133e27eSPeter Avalos 				return (1);
4821133e27eSPeter Avalos 		}
4831133e27eSPeter Avalos 		return (0);
4841133e27eSPeter Avalos 	}
4851133e27eSPeter Avalos 	/*
4861133e27eSPeter Avalos 	 * Set read pointer.
4871133e27eSPeter Avalos 	 */
4881133e27eSPeter Avalos 	ch_block = new_block;
4891133e27eSPeter Avalos 	ch_offset = pos % LBUFSIZE;
4901133e27eSPeter Avalos 	return (0);
4911133e27eSPeter Avalos }
4921133e27eSPeter Avalos 
4931133e27eSPeter Avalos /*
4941133e27eSPeter Avalos  * Seek to the end of the file.
4951133e27eSPeter Avalos  */
ch_end_seek(void)496*320d7c8aSAaron LI public int ch_end_seek(void)
4971133e27eSPeter Avalos {
4981133e27eSPeter Avalos 	POSITION len;
4991133e27eSPeter Avalos 
5001133e27eSPeter Avalos 	if (thisfile == NULL)
5011133e27eSPeter Avalos 		return (0);
5021133e27eSPeter Avalos 
5031133e27eSPeter Avalos 	if (ch_flags & CH_CANSEEK)
5041133e27eSPeter Avalos 		ch_fsize = filesize(ch_file);
5051133e27eSPeter Avalos 
5061133e27eSPeter Avalos 	len = ch_length();
5071133e27eSPeter Avalos 	if (len != NULL_POSITION)
5081133e27eSPeter Avalos 		return (ch_seek(len));
5091133e27eSPeter Avalos 
5101133e27eSPeter Avalos 	/*
5111133e27eSPeter Avalos 	 * Do it the slow way: read till end of data.
5121133e27eSPeter Avalos 	 */
5131133e27eSPeter Avalos 	while (ch_forw_get() != EOI)
5141133e27eSPeter Avalos 		if (ABORT_SIGS())
5151133e27eSPeter Avalos 			return (1);
5161133e27eSPeter Avalos 	return (0);
5171133e27eSPeter Avalos }
5181133e27eSPeter Avalos 
5191133e27eSPeter Avalos /*
520fa0be7c5SJohn Marino  * Seek to the last position in the file that is currently buffered.
521fa0be7c5SJohn Marino  */
ch_end_buffer_seek(void)522*320d7c8aSAaron LI public int ch_end_buffer_seek(void)
523fa0be7c5SJohn Marino {
52402d62a0fSDaniel Fojt 	struct buf *bp;
52502d62a0fSDaniel Fojt 	struct bufnode *bn;
526fa0be7c5SJohn Marino 	POSITION buf_pos;
527fa0be7c5SJohn Marino 	POSITION end_pos;
528fa0be7c5SJohn Marino 
529fa0be7c5SJohn Marino 	if (thisfile == NULL || (ch_flags & CH_CANSEEK))
530fa0be7c5SJohn Marino 		return (ch_end_seek());
531fa0be7c5SJohn Marino 
532fa0be7c5SJohn Marino 	end_pos = 0;
533fa0be7c5SJohn Marino 	FOR_BUFS(bn)
534fa0be7c5SJohn Marino 	{
535fa0be7c5SJohn Marino 		bp = bufnode_buf(bn);
536fa0be7c5SJohn Marino 		buf_pos = (bp->block * LBUFSIZE) + bp->datasize;
537fa0be7c5SJohn Marino 		if (buf_pos > end_pos)
538fa0be7c5SJohn Marino 			end_pos = buf_pos;
539fa0be7c5SJohn Marino 	}
540fa0be7c5SJohn Marino 
541fa0be7c5SJohn Marino 	return (ch_seek(end_pos));
542fa0be7c5SJohn Marino }
543fa0be7c5SJohn Marino 
544fa0be7c5SJohn Marino /*
5451133e27eSPeter Avalos  * Seek to the beginning of the file, or as close to it as we can get.
5461133e27eSPeter Avalos  * We may not be able to seek there if input is a pipe and the
5471133e27eSPeter Avalos  * beginning of the pipe is no longer buffered.
5481133e27eSPeter Avalos  */
ch_beg_seek(void)549*320d7c8aSAaron LI public int ch_beg_seek(void)
5501133e27eSPeter Avalos {
55102d62a0fSDaniel Fojt 	struct bufnode *bn;
55202d62a0fSDaniel Fojt 	struct bufnode *firstbn;
5531133e27eSPeter Avalos 
5541133e27eSPeter Avalos 	/*
5551133e27eSPeter Avalos 	 * Try a plain ch_seek first.
5561133e27eSPeter Avalos 	 */
5571133e27eSPeter Avalos 	if (ch_seek(ch_zero()) == 0)
5581133e27eSPeter Avalos 		return (0);
5591133e27eSPeter Avalos 
5601133e27eSPeter Avalos 	/*
5611133e27eSPeter Avalos 	 * Can't get to position 0.
5621133e27eSPeter Avalos 	 * Look thru the buffers for the one closest to position 0.
5631133e27eSPeter Avalos 	 */
5648be36e5bSPeter Avalos 	firstbn = ch_bufhead;
5658be36e5bSPeter Avalos 	if (firstbn == END_OF_CHAIN)
5661133e27eSPeter Avalos 		return (1);
5678be36e5bSPeter Avalos 	FOR_BUFS(bn)
5688be36e5bSPeter Avalos 	{
5698be36e5bSPeter Avalos 		if (bufnode_buf(bn)->block < bufnode_buf(firstbn)->block)
5708be36e5bSPeter Avalos 			firstbn = bn;
5718be36e5bSPeter Avalos 	}
5728be36e5bSPeter Avalos 	ch_block = bufnode_buf(firstbn)->block;
5731133e27eSPeter Avalos 	ch_offset = 0;
5741133e27eSPeter Avalos 	return (0);
5751133e27eSPeter Avalos }
5761133e27eSPeter Avalos 
5771133e27eSPeter Avalos /*
5781133e27eSPeter Avalos  * Return the length of the file, if known.
5791133e27eSPeter Avalos  */
ch_length(void)580*320d7c8aSAaron LI public POSITION ch_length(void)
5811133e27eSPeter Avalos {
5821133e27eSPeter Avalos 	if (thisfile == NULL)
5831133e27eSPeter Avalos 		return (NULL_POSITION);
5841133e27eSPeter Avalos 	if (ignore_eoi)
5851133e27eSPeter Avalos 		return (NULL_POSITION);
5861133e27eSPeter Avalos 	if (ch_flags & CH_HELPFILE)
5871133e27eSPeter Avalos 		return (size_helpdata);
588e639dc31SJohn Marino 	if (ch_flags & CH_NODATA)
589e639dc31SJohn Marino 		return (0);
5901133e27eSPeter Avalos 	return (ch_fsize);
5911133e27eSPeter Avalos }
5921133e27eSPeter Avalos 
5931133e27eSPeter Avalos /*
5941133e27eSPeter Avalos  * Return the current position in the file.
5951133e27eSPeter Avalos  */
ch_tell(void)596*320d7c8aSAaron LI public POSITION ch_tell(void)
5971133e27eSPeter Avalos {
5981133e27eSPeter Avalos 	if (thisfile == NULL)
5991133e27eSPeter Avalos 		return (NULL_POSITION);
6001133e27eSPeter Avalos 	return (ch_block * LBUFSIZE) + ch_offset;
6011133e27eSPeter Avalos }
6021133e27eSPeter Avalos 
6031133e27eSPeter Avalos /*
6041133e27eSPeter Avalos  * Get the current char and post-increment the read pointer.
6051133e27eSPeter Avalos  */
ch_forw_get(void)606*320d7c8aSAaron LI public int ch_forw_get(void)
6071133e27eSPeter Avalos {
60802d62a0fSDaniel Fojt 	int c;
6091133e27eSPeter Avalos 
6101133e27eSPeter Avalos 	if (thisfile == NULL)
6111133e27eSPeter Avalos 		return (EOI);
6121133e27eSPeter Avalos 	c = ch_get();
6131133e27eSPeter Avalos 	if (c == EOI)
6141133e27eSPeter Avalos 		return (EOI);
6151133e27eSPeter Avalos 	if (ch_offset < LBUFSIZE-1)
6161133e27eSPeter Avalos 		ch_offset++;
6171133e27eSPeter Avalos 	else
6181133e27eSPeter Avalos 	{
6191133e27eSPeter Avalos 		ch_block ++;
6201133e27eSPeter Avalos 		ch_offset = 0;
6211133e27eSPeter Avalos 	}
6221133e27eSPeter Avalos 	return (c);
6231133e27eSPeter Avalos }
6241133e27eSPeter Avalos 
6251133e27eSPeter Avalos /*
6261133e27eSPeter Avalos  * Pre-decrement the read pointer and get the new current char.
6271133e27eSPeter Avalos  */
ch_back_get(void)628*320d7c8aSAaron LI public int ch_back_get(void)
6291133e27eSPeter Avalos {
6301133e27eSPeter Avalos 	if (thisfile == NULL)
6311133e27eSPeter Avalos 		return (EOI);
6321133e27eSPeter Avalos 	if (ch_offset > 0)
6331133e27eSPeter Avalos 		ch_offset --;
6341133e27eSPeter Avalos 	else
6351133e27eSPeter Avalos 	{
6361133e27eSPeter Avalos 		if (ch_block <= 0)
6371133e27eSPeter Avalos 			return (EOI);
6381133e27eSPeter Avalos 		if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1))
6391133e27eSPeter Avalos 			return (EOI);
6401133e27eSPeter Avalos 		ch_block--;
6411133e27eSPeter Avalos 		ch_offset = LBUFSIZE-1;
6421133e27eSPeter Avalos 	}
6431133e27eSPeter Avalos 	return (ch_get());
6441133e27eSPeter Avalos }
6451133e27eSPeter Avalos 
6461133e27eSPeter Avalos /*
6471133e27eSPeter Avalos  * Set max amount of buffer space.
6481133e27eSPeter Avalos  * bufspace is in units of 1024 bytes.  -1 mean no limit.
6491133e27eSPeter Avalos  */
ch_setbufspace(int bufspace)650*320d7c8aSAaron LI public void ch_setbufspace(int bufspace)
6511133e27eSPeter Avalos {
6521133e27eSPeter Avalos 	if (bufspace < 0)
6531133e27eSPeter Avalos 		maxbufs = -1;
6541133e27eSPeter Avalos 	else
6551133e27eSPeter Avalos 	{
656*320d7c8aSAaron LI 		int lbufk = LBUFSIZE / 1024;
657*320d7c8aSAaron LI 		maxbufs = bufspace / lbufk + (bufspace % lbufk != 0);
6581133e27eSPeter Avalos 		if (maxbufs < 1)
6591133e27eSPeter Avalos 			maxbufs = 1;
6601133e27eSPeter Avalos 	}
6611133e27eSPeter Avalos }
6621133e27eSPeter Avalos 
6631133e27eSPeter Avalos /*
6641133e27eSPeter Avalos  * Flush (discard) any saved file state, including buffer contents.
6651133e27eSPeter Avalos  */
ch_flush(void)666*320d7c8aSAaron LI public void ch_flush(void)
6671133e27eSPeter Avalos {
66802d62a0fSDaniel Fojt 	struct bufnode *bn;
6691133e27eSPeter Avalos 
6701133e27eSPeter Avalos 	if (thisfile == NULL)
6711133e27eSPeter Avalos 		return;
6721133e27eSPeter Avalos 
6731133e27eSPeter Avalos 	if (!(ch_flags & CH_CANSEEK))
6741133e27eSPeter Avalos 	{
6751133e27eSPeter Avalos 		/*
6761133e27eSPeter Avalos 		 * If input is a pipe, we don't flush buffer contents,
6771133e27eSPeter Avalos 		 * since the contents can't be recovered.
6781133e27eSPeter Avalos 		 */
6791133e27eSPeter Avalos 		ch_fsize = NULL_POSITION;
6801133e27eSPeter Avalos 		return;
6811133e27eSPeter Avalos 	}
6821133e27eSPeter Avalos 
6831133e27eSPeter Avalos 	/*
6841133e27eSPeter Avalos 	 * Initialize all the buffers.
6851133e27eSPeter Avalos 	 */
6868be36e5bSPeter Avalos 	FOR_BUFS(bn)
6878be36e5bSPeter Avalos 	{
6888be36e5bSPeter Avalos 		bufnode_buf(bn)->block = -1;
6898be36e5bSPeter Avalos 	}
6901133e27eSPeter Avalos 
6911133e27eSPeter Avalos 	/*
6921133e27eSPeter Avalos 	 * Figure out the size of the file, if we can.
6931133e27eSPeter Avalos 	 */
6941133e27eSPeter Avalos 	ch_fsize = filesize(ch_file);
6951133e27eSPeter Avalos 
6961133e27eSPeter Avalos 	/*
6971133e27eSPeter Avalos 	 * Seek to a known position: the beginning of the file.
6981133e27eSPeter Avalos 	 */
6991133e27eSPeter Avalos 	ch_fpos = 0;
7001133e27eSPeter Avalos 	ch_block = 0; /* ch_fpos / LBUFSIZE; */
7011133e27eSPeter Avalos 	ch_offset = 0; /* ch_fpos % LBUFSIZE; */
7021133e27eSPeter Avalos 
7030c7ad07eSAntonio Huete Jimenez #if HAVE_PROCFS
7041133e27eSPeter Avalos 	/*
7051133e27eSPeter Avalos 	 * This is a kludge to workaround a Linux kernel bug: files in
7061133e27eSPeter Avalos 	 * /proc have a size of 0 according to fstat() but have readable
7071133e27eSPeter Avalos 	 * data.  They are sometimes, but not always, seekable.
7081133e27eSPeter Avalos 	 * Force them to be non-seekable here.
7091133e27eSPeter Avalos 	 */
7101133e27eSPeter Avalos 	if (ch_fsize == 0)
7111133e27eSPeter Avalos 	{
7120c7ad07eSAntonio Huete Jimenez 		struct statfs st;
7130c7ad07eSAntonio Huete Jimenez 		if (fstatfs(ch_file, &st) == 0)
7140c7ad07eSAntonio Huete Jimenez 		{
7150c7ad07eSAntonio Huete Jimenez 			if (st.f_type == PROC_SUPER_MAGIC)
7160c7ad07eSAntonio Huete Jimenez 			{
7171133e27eSPeter Avalos 				ch_fsize = NULL_POSITION;
7181133e27eSPeter Avalos 				ch_flags &= ~CH_CANSEEK;
7191133e27eSPeter Avalos 			}
7200c7ad07eSAntonio Huete Jimenez 		}
7210c7ad07eSAntonio Huete Jimenez 	}
7221133e27eSPeter Avalos #endif
7231133e27eSPeter Avalos 
7241133e27eSPeter Avalos 	if (lseek(ch_file, (off_t)0, SEEK_SET) == BAD_LSEEK)
7251133e27eSPeter Avalos 	{
7261133e27eSPeter Avalos 		/*
7271133e27eSPeter Avalos 		 * Warning only; even if the seek fails for some reason,
7281133e27eSPeter Avalos 		 * there's a good chance we're at the beginning anyway.
7291133e27eSPeter Avalos 		 * {{ I think this is bogus reasoning. }}
7301133e27eSPeter Avalos 		 */
7311133e27eSPeter Avalos 		error("seek error to 0", NULL_PARG);
7321133e27eSPeter Avalos 	}
7331133e27eSPeter Avalos }
7341133e27eSPeter Avalos 
7351133e27eSPeter Avalos /*
7361133e27eSPeter Avalos  * Allocate a new buffer.
7371133e27eSPeter Avalos  * The buffer is added to the tail of the buffer chain.
7381133e27eSPeter Avalos  */
ch_addbuf(void)739*320d7c8aSAaron LI static int ch_addbuf(void)
7401133e27eSPeter Avalos {
74102d62a0fSDaniel Fojt 	struct buf *bp;
74202d62a0fSDaniel Fojt 	struct bufnode *bn;
7431133e27eSPeter Avalos 
7441133e27eSPeter Avalos 	/*
7451133e27eSPeter Avalos 	 * Allocate and initialize a new buffer and link it
7461133e27eSPeter Avalos 	 * onto the tail of the buffer list.
7471133e27eSPeter Avalos 	 */
7481133e27eSPeter Avalos 	bp = (struct buf *) calloc(1, sizeof(struct buf));
7491133e27eSPeter Avalos 	if (bp == NULL)
7501133e27eSPeter Avalos 		return (1);
7511133e27eSPeter Avalos 	ch_nbufs++;
7521133e27eSPeter Avalos 	bp->block = -1;
7538be36e5bSPeter Avalos 	bn = &bp->node;
7548be36e5bSPeter Avalos 
7558be36e5bSPeter Avalos 	BUF_INS_TAIL(bn);
7568be36e5bSPeter Avalos 	BUF_HASH_INS(bn, 0);
7571133e27eSPeter Avalos 	return (0);
7581133e27eSPeter Avalos }
7591133e27eSPeter Avalos 
7601133e27eSPeter Avalos /*
7611133e27eSPeter Avalos  *
7621133e27eSPeter Avalos  */
init_hashtbl(void)763*320d7c8aSAaron LI static void init_hashtbl(void)
7641133e27eSPeter Avalos {
76502d62a0fSDaniel Fojt 	int h;
7661133e27eSPeter Avalos 
7671133e27eSPeter Avalos 	for (h = 0;  h < BUFHASH_SIZE;  h++)
7681133e27eSPeter Avalos 	{
7698be36e5bSPeter Avalos 		thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h);
7708be36e5bSPeter Avalos 		thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h);
7711133e27eSPeter Avalos 	}
7721133e27eSPeter Avalos }
7731133e27eSPeter Avalos 
7741133e27eSPeter Avalos /*
7751133e27eSPeter Avalos  * Delete all buffers for this file.
7761133e27eSPeter Avalos  */
ch_delbufs(void)777*320d7c8aSAaron LI static void ch_delbufs(void)
7781133e27eSPeter Avalos {
77902d62a0fSDaniel Fojt 	struct bufnode *bn;
7801133e27eSPeter Avalos 
7811133e27eSPeter Avalos 	while (ch_bufhead != END_OF_CHAIN)
7821133e27eSPeter Avalos 	{
7838be36e5bSPeter Avalos 		bn = ch_bufhead;
7848be36e5bSPeter Avalos 		BUF_RM(bn);
7858be36e5bSPeter Avalos 		free(bufnode_buf(bn));
7861133e27eSPeter Avalos 	}
7871133e27eSPeter Avalos 	ch_nbufs = 0;
7881133e27eSPeter Avalos 	init_hashtbl();
7891133e27eSPeter Avalos }
7901133e27eSPeter Avalos 
7911133e27eSPeter Avalos /*
7921133e27eSPeter Avalos  * Is it possible to seek on a file descriptor?
7931133e27eSPeter Avalos  */
seekable(int f)794*320d7c8aSAaron LI public int seekable(int f)
7951133e27eSPeter Avalos {
7961133e27eSPeter Avalos #if MSDOS_COMPILER
7971133e27eSPeter Avalos 	extern int fd0;
7981133e27eSPeter Avalos 	if (f == fd0 && !isatty(fd0))
7991133e27eSPeter Avalos 	{
8001133e27eSPeter Avalos 		/*
8011133e27eSPeter Avalos 		 * In MS-DOS, pipes are seekable.  Check for
8021133e27eSPeter Avalos 		 * standard input, and pretend it is not seekable.
8031133e27eSPeter Avalos 		 */
8041133e27eSPeter Avalos 		return (0);
8051133e27eSPeter Avalos 	}
8061133e27eSPeter Avalos #endif
8071133e27eSPeter Avalos 	return (lseek(f, (off_t)1, SEEK_SET) != BAD_LSEEK);
8081133e27eSPeter Avalos }
8091133e27eSPeter Avalos 
8101133e27eSPeter Avalos /*
811e639dc31SJohn Marino  * Force EOF to be at the current read position.
812e639dc31SJohn Marino  * This is used after an ignore_eof read, during which the EOF may change.
813e639dc31SJohn Marino  */
ch_set_eof(void)814*320d7c8aSAaron LI public void ch_set_eof(void)
815e639dc31SJohn Marino {
8160c7ad07eSAntonio Huete Jimenez 	if (ch_fsize != NULL_POSITION && ch_fsize < ch_fpos)
817e639dc31SJohn Marino 		ch_fsize = ch_fpos;
818e639dc31SJohn Marino }
819e639dc31SJohn Marino 
820e639dc31SJohn Marino 
821e639dc31SJohn Marino /*
8221133e27eSPeter Avalos  * Initialize file state for a new file.
8231133e27eSPeter Avalos  */
ch_init(int f,int flags)824*320d7c8aSAaron LI public void ch_init(int f, int flags)
8251133e27eSPeter Avalos {
8261133e27eSPeter Avalos 	/*
8271133e27eSPeter Avalos 	 * See if we already have a filestate for this file.
8281133e27eSPeter Avalos 	 */
8291133e27eSPeter Avalos 	thisfile = (struct filestate *) get_filestate(curr_ifile);
8301133e27eSPeter Avalos 	if (thisfile == NULL)
8311133e27eSPeter Avalos 	{
8321133e27eSPeter Avalos 		/*
8331133e27eSPeter Avalos 		 * Allocate and initialize a new filestate.
8341133e27eSPeter Avalos 		 */
8351133e27eSPeter Avalos 		thisfile = (struct filestate *)
8360c7ad07eSAntonio Huete Jimenez 				ecalloc(1, sizeof(struct filestate));
8378be36e5bSPeter Avalos 		thisfile->buflist.next = thisfile->buflist.prev = END_OF_CHAIN;
8381133e27eSPeter Avalos 		thisfile->nbufs = 0;
83902d62a0fSDaniel Fojt 		thisfile->flags = flags;
8401133e27eSPeter Avalos 		thisfile->fpos = 0;
8411133e27eSPeter Avalos 		thisfile->block = 0;
8421133e27eSPeter Avalos 		thisfile->offset = 0;
8431133e27eSPeter Avalos 		thisfile->file = -1;
8441133e27eSPeter Avalos 		thisfile->fsize = NULL_POSITION;
8451133e27eSPeter Avalos 		init_hashtbl();
8461133e27eSPeter Avalos 		/*
8471133e27eSPeter Avalos 		 * Try to seek; set CH_CANSEEK if it works.
8481133e27eSPeter Avalos 		 */
8491133e27eSPeter Avalos 		if ((flags & CH_CANSEEK) && !seekable(f))
8501133e27eSPeter Avalos 			ch_flags &= ~CH_CANSEEK;
8511133e27eSPeter Avalos 		set_filestate(curr_ifile, (void *) thisfile);
8521133e27eSPeter Avalos 	}
8531133e27eSPeter Avalos 	if (thisfile->file == -1)
8541133e27eSPeter Avalos 		thisfile->file = f;
8551133e27eSPeter Avalos 	ch_flush();
8561133e27eSPeter Avalos }
8571133e27eSPeter Avalos 
8581133e27eSPeter Avalos /*
8591133e27eSPeter Avalos  * Close a filestate.
8601133e27eSPeter Avalos  */
ch_close(void)861*320d7c8aSAaron LI public void ch_close(void)
8621133e27eSPeter Avalos {
8631133e27eSPeter Avalos 	int keepstate = FALSE;
8641133e27eSPeter Avalos 
8651133e27eSPeter Avalos 	if (thisfile == NULL)
8661133e27eSPeter Avalos 		return;
8671133e27eSPeter Avalos 
86802d62a0fSDaniel Fojt 	if ((ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE)) && !(ch_flags & CH_KEEPOPEN))
8691133e27eSPeter Avalos 	{
8701133e27eSPeter Avalos 		/*
8711133e27eSPeter Avalos 		 * We can seek or re-open, so we don't need to keep buffers.
8721133e27eSPeter Avalos 		 */
8731133e27eSPeter Avalos 		ch_delbufs();
8741133e27eSPeter Avalos 	} else
8751133e27eSPeter Avalos 		keepstate = TRUE;
8761133e27eSPeter Avalos 	if (!(ch_flags & CH_KEEPOPEN))
8771133e27eSPeter Avalos 	{
8781133e27eSPeter Avalos 		/*
8791133e27eSPeter Avalos 		 * We don't need to keep the file descriptor open
8801133e27eSPeter Avalos 		 * (because we can re-open it.)
8811133e27eSPeter Avalos 		 * But don't really close it if it was opened via popen(),
8821133e27eSPeter Avalos 		 * because pclose() wants to close it.
8831133e27eSPeter Avalos 		 */
8841133e27eSPeter Avalos 		if (!(ch_flags & (CH_POPENED|CH_HELPFILE)))
8851133e27eSPeter Avalos 			close(ch_file);
8861133e27eSPeter Avalos 		ch_file = -1;
8871133e27eSPeter Avalos 	} else
8881133e27eSPeter Avalos 		keepstate = TRUE;
8891133e27eSPeter Avalos 	if (!keepstate)
8901133e27eSPeter Avalos 	{
8911133e27eSPeter Avalos 		/*
8921133e27eSPeter Avalos 		 * We don't even need to keep the filestate structure.
8931133e27eSPeter Avalos 		 */
8941133e27eSPeter Avalos 		free(thisfile);
8951133e27eSPeter Avalos 		thisfile = NULL;
8961133e27eSPeter Avalos 		set_filestate(curr_ifile, (void *) NULL);
8971133e27eSPeter Avalos 	}
8981133e27eSPeter Avalos }
8991133e27eSPeter Avalos 
9001133e27eSPeter Avalos /*
9011133e27eSPeter Avalos  * Return ch_flags for the current file.
9021133e27eSPeter Avalos  */
ch_getflags(void)903*320d7c8aSAaron LI public int ch_getflags(void)
9041133e27eSPeter Avalos {
9051133e27eSPeter Avalos 	if (thisfile == NULL)
9061133e27eSPeter Avalos 		return (0);
9071133e27eSPeter Avalos 	return (ch_flags);
9081133e27eSPeter Avalos }
9091133e27eSPeter Avalos 
9101133e27eSPeter Avalos #if 0
911*320d7c8aSAaron LI static void ch_dump(struct filestate *fs)
9121133e27eSPeter Avalos {
9131133e27eSPeter Avalos 	struct buf *bp;
9148be36e5bSPeter Avalos 	struct bufnode *bn;
9151133e27eSPeter Avalos 	unsigned char *s;
9161133e27eSPeter Avalos 
9171133e27eSPeter Avalos 	if (fs == NULL)
9181133e27eSPeter Avalos 	{
9191133e27eSPeter Avalos 		printf(" --no filestate\n");
9201133e27eSPeter Avalos 		return;
9211133e27eSPeter Avalos 	}
9221133e27eSPeter Avalos 	printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n",
9231133e27eSPeter Avalos 		fs->file, fs->flags, fs->fpos,
9241133e27eSPeter Avalos 		fs->fsize, fs->block, fs->offset);
9251133e27eSPeter Avalos 	printf(" %d bufs:\n", fs->nbufs);
9268be36e5bSPeter Avalos 	for (bn = fs->next; bn != &fs->buflist;  bn = bn->next)
9271133e27eSPeter Avalos 	{
9288be36e5bSPeter Avalos 		bp = bufnode_buf(bn);
9291133e27eSPeter Avalos 		printf("%x: blk %x, size %x \"",
9301133e27eSPeter Avalos 			bp, bp->block, bp->datasize);
9311133e27eSPeter Avalos 		for (s = bp->data;  s < bp->data + 30;  s++)
9321133e27eSPeter Avalos 			if (*s >= ' ' && *s < 0x7F)
9331133e27eSPeter Avalos 				printf("%c", *s);
9341133e27eSPeter Avalos 			else
9351133e27eSPeter Avalos 				printf(".");
9361133e27eSPeter Avalos 		printf("\"\n");
9371133e27eSPeter Avalos 	}
9381133e27eSPeter Avalos }
9391133e27eSPeter Avalos #endif
940