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 26extern IFILE curr_ifile; 27 28struct 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 */ 52static struct ifile anchor = { &anchor, &anchor, NULL, NULL, NULL, 0, 0, '\0', 53 { NULL_POSITION, 0 } }; 54static int ifiles = 0; 55 56static 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 */ 65static 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 */ 88static 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 */ 102static 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 */ 131public 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 */ 154public 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 */ 167public 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 */ 180public 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 */ 194public int nifile(void) 195{ 196 return (ifiles); 197} 198 199/* 200 * Find an ifile structure, given a filename. 201 */ 202static 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 */ 234public 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 */ 246public 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 */ 256public 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 */ 266public 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 */ 274public 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 */ 283public 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 */ 291public 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 */ 299public int opened(IFILE ifile) 300{ 301 return (int_ifile(ifile)->h_opened); 302} 303 304public void hold_ifile(IFILE ifile, int incr) 305{ 306 int_ifile(ifile)->h_hold += incr; 307} 308 309public int held_ifile(IFILE ifile) 310{ 311 return (int_ifile(ifile)->h_hold); 312} 313 314public void * get_filestate(IFILE ifile) 315{ 316 return (int_ifile(ifile)->h_filestate); 317} 318 319public void set_filestate(IFILE ifile, void *filestate) 320{ 321 int_ifile(ifile)->h_filestate = filestate; 322} 323 324public void set_altpipe(IFILE ifile, void *p) 325{ 326 int_ifile(ifile)->h_altpipe = p; 327} 328 329public void *get_altpipe(IFILE ifile) 330{ 331 return (int_ifile(ifile)->h_altpipe); 332} 333 334public 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 342public char * get_altfilename(IFILE ifile) 343{ 344 return (int_ifile(ifile)->h_altfilename); 345} 346 347#if 0 348public 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