1/*
2 * Copyright (C) 1984-2017  Mark Nudelman
3 *
4 * You may distribute under the terms of either the GNU General Public
5 * License or the Less License, as specified in the README file.
6 *
7 * For more information, see the README file.
8 */
9
10
11/*
12 * An IFILE represents an input file.
13 *
14 * It is actually a pointer to an ifile structure,
15 * but is opaque outside this module.
16 * Ifile structures are kept in a linked list in the order they
17 * appear on the command line.
18 * Any new file which does not already appear in the list is
19 * inserted after the current file.
20 */
21
22#include "less.h"
23
24extern IFILE	curr_ifile;
25
26struct ifile {
27	struct ifile *h_next;           /* Links for command line list */
28	struct ifile *h_prev;
29	char *h_filename;               /* Name of the file */
30	void *h_filestate;              /* File state (used in ch.c) */
31	int h_index;                    /* Index within command line list */
32	int h_hold;                     /* Hold count */
33	char h_opened;                  /* Has this ifile been opened? */
34	struct scrpos h_scrpos;         /* Saved position within the file */
35	void *h_altpipe;                /* Alt pipe */
36	char *h_altfilename;            /* Alt filename */
37};
38
39/*
40 * Convert an IFILE (external representation)
41 * to a struct file (internal representation), and vice versa.
42 */
43#define int_ifile(h)	((struct ifile *)(h))
44#define ext_ifile(h)	((IFILE)(h))
45
46/*
47 * Anchor for linked list.
48 */
49static struct ifile anchor = { &anchor, &anchor, NULL, NULL, 0, 0, '\0',
50				{ NULL_POSITION, 0 } };
51static int ifiles = 0;
52
53	static void
54incr_index(p, incr)
55	struct ifile *p;
56	int incr;
57{
58	for (;  p != &anchor;  p = p->h_next)
59		p->h_index += incr;
60}
61
62/*
63 * Link an ifile into the ifile list.
64 */
65	static void
66link_ifile(p, prev)
67	struct ifile *p;
68	struct ifile *prev;
69{
70	/*
71	 * Link into list.
72	 */
73	if (prev == NULL)
74		prev = &anchor;
75	p->h_next = prev->h_next;
76	p->h_prev = prev;
77	prev->h_next->h_prev = p;
78	prev->h_next = p;
79	/*
80	 * Calculate index for the new one,
81	 * and adjust the indexes for subsequent ifiles in the list.
82	 */
83	p->h_index = prev->h_index + 1;
84	incr_index(p->h_next, 1);
85	ifiles++;
86}
87
88/*
89 * Unlink an ifile from the ifile list.
90 */
91	static void
92unlink_ifile(p)
93	struct ifile *p;
94{
95	p->h_next->h_prev = p->h_prev;
96	p->h_prev->h_next = p->h_next;
97	incr_index(p->h_next, -1);
98	ifiles--;
99}
100
101/*
102 * Allocate a new ifile structure and stick a filename in it.
103 * It should go after "prev" in the list
104 * (or at the beginning of the list if "prev" is NULL).
105 * Return a pointer to the new ifile structure.
106 */
107	static struct ifile *
108new_ifile(filename, prev)
109	char *filename;
110	struct ifile *prev;
111{
112	struct ifile *p;
113
114	/*
115	 * Allocate and initialize structure.
116	 */
117	p = (struct ifile *) ecalloc(1, sizeof(struct ifile));
118	p->h_filename = save(filename);
119	p->h_scrpos.pos = NULL_POSITION;
120	p->h_opened = 0;
121	p->h_hold = 0;
122	p->h_filestate = NULL;
123	link_ifile(p, prev);
124	return (p);
125}
126
127/*
128 * Delete an existing ifile structure.
129 */
130	public void
131del_ifile(h)
132	IFILE h;
133{
134	struct ifile *p;
135
136	if (h == NULL_IFILE)
137		return;
138	/*
139	 * If the ifile we're deleting is the currently open ifile,
140	 * move off it.
141	 */
142	unmark(h);
143	if (h == curr_ifile)
144		curr_ifile = getoff_ifile(curr_ifile);
145	p = int_ifile(h);
146	unlink_ifile(p);
147	free(p->h_filename);
148	free(p);
149}
150
151/*
152 * Get the ifile after a given one in the list.
153 */
154	public IFILE
155next_ifile(h)
156	IFILE h;
157{
158	struct ifile *p;
159
160	p = (h == NULL_IFILE) ? &anchor : int_ifile(h);
161	if (p->h_next == &anchor)
162		return (NULL_IFILE);
163	return (ext_ifile(p->h_next));
164}
165
166/*
167 * Get the ifile before a given one in the list.
168 */
169	public IFILE
170prev_ifile(h)
171	IFILE h;
172{
173	struct ifile *p;
174
175	p = (h == NULL_IFILE) ? &anchor : int_ifile(h);
176	if (p->h_prev == &anchor)
177		return (NULL_IFILE);
178	return (ext_ifile(p->h_prev));
179}
180
181/*
182 * Return a different ifile from the given one.
183 */
184	public IFILE
185getoff_ifile(ifile)
186	IFILE ifile;
187{
188	IFILE newifile;
189
190	if ((newifile = prev_ifile(ifile)) != NULL_IFILE)
191		return (newifile);
192	if ((newifile = next_ifile(ifile)) != NULL_IFILE)
193		return (newifile);
194	return (NULL_IFILE);
195}
196
197/*
198 * Return the number of ifiles.
199 */
200	public int
201nifile()
202{
203	return (ifiles);
204}
205
206/*
207 * Find an ifile structure, given a filename.
208 */
209	static struct ifile *
210find_ifile(filename)
211	char *filename;
212{
213	struct ifile *p;
214
215	for (p = anchor.h_next;  p != &anchor;  p = p->h_next)
216		if (strcmp(filename, p->h_filename) == 0)
217			return (p);
218	return (NULL);
219}
220
221/*
222 * Get the ifile associated with a filename.
223 * If the filename has not been seen before,
224 * insert the new ifile after "prev" in the list.
225 */
226	public IFILE
227get_ifile(filename, prev)
228	char *filename;
229	IFILE prev;
230{
231	struct ifile *p;
232
233	if ((p = find_ifile(filename)) == NULL)
234		p = new_ifile(filename, int_ifile(prev));
235	return (ext_ifile(p));
236}
237
238/*
239 * Get the filename associated with a ifile.
240 */
241	public char *
242get_filename(ifile)
243	IFILE ifile;
244{
245	if (ifile == NULL)
246		return (NULL);
247	return (int_ifile(ifile)->h_filename);
248}
249
250/*
251 * Get the index of the file associated with a ifile.
252 */
253	public int
254get_index(ifile)
255	IFILE ifile;
256{
257	return (int_ifile(ifile)->h_index);
258}
259
260/*
261 * Save the file position to be associated with a given file.
262 */
263	public void
264store_pos(ifile, scrpos)
265	IFILE ifile;
266	struct scrpos *scrpos;
267{
268	int_ifile(ifile)->h_scrpos = *scrpos;
269}
270
271/*
272 * Recall the file position associated with a file.
273 * If no position has been associated with the file, return NULL_POSITION.
274 */
275	public void
276get_pos(ifile, scrpos)
277	IFILE ifile;
278	struct scrpos *scrpos;
279{
280	*scrpos = int_ifile(ifile)->h_scrpos;
281}
282
283/*
284 * Mark the ifile as "opened".
285 */
286	public void
287set_open(ifile)
288	IFILE ifile;
289{
290	int_ifile(ifile)->h_opened = 1;
291}
292
293/*
294 * Return whether the ifile has been opened previously.
295 */
296	public int
297opened(ifile)
298	IFILE ifile;
299{
300	return (int_ifile(ifile)->h_opened);
301}
302
303	public void
304hold_ifile(ifile, incr)
305	IFILE ifile;
306	int incr;
307{
308	int_ifile(ifile)->h_hold += incr;
309}
310
311	public int
312held_ifile(ifile)
313	IFILE ifile;
314{
315	return (int_ifile(ifile)->h_hold);
316}
317
318	public void *
319get_filestate(ifile)
320	IFILE ifile;
321{
322	return (int_ifile(ifile)->h_filestate);
323}
324
325	public void
326set_filestate(ifile, filestate)
327	IFILE ifile;
328	void *filestate;
329{
330	int_ifile(ifile)->h_filestate = filestate;
331}
332
333	public void
334set_altpipe(ifile, p)
335	IFILE ifile;
336	void *p;
337{
338	int_ifile(ifile)->h_altpipe = p;
339}
340
341	public void *
342get_altpipe(ifile)
343	IFILE ifile;
344{
345	return (int_ifile(ifile)->h_altpipe);
346}
347
348	public void
349set_altfilename(ifile, altfilename)
350	IFILE ifile;
351	char *altfilename;
352{
353	struct ifile *p = int_ifile(ifile);
354	if (p->h_altfilename != NULL)
355		free(p->h_altfilename);
356	p->h_altfilename = altfilename;
357}
358
359	public char *
360get_altfilename(ifile)
361	IFILE ifile;
362{
363	return (int_ifile(ifile)->h_altfilename);
364}
365
366#if 0
367	public void
368if_dump()
369{
370	struct ifile *p;
371
372	for (p = anchor.h_next;  p != &anchor;  p = p->h_next)
373	{
374		printf("%x: %d. <%s> pos %d,%x\n",
375			p, p->h_index, p->h_filename,
376			p->h_scrpos.ln, p->h_scrpos.pos);
377		ch_dump(p->h_filestate);
378	}
379}
380#endif
381