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