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