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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 194 public int nifile(void) 195 { 196 return (ifiles); 197 } 198 199 /* 200 * Find an ifile structure, given a filename. 201 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 299 public int opened(IFILE ifile) 300 { 301 return (int_ifile(ifile)->h_opened); 302 } 303 304 public void hold_ifile(IFILE ifile, int incr) 305 { 306 int_ifile(ifile)->h_hold += incr; 307 } 308 309 public int held_ifile(IFILE ifile) 310 { 311 return (int_ifile(ifile)->h_hold); 312 } 313 314 public void * get_filestate(IFILE ifile) 315 { 316 return (int_ifile(ifile)->h_filestate); 317 } 318 319 public void set_filestate(IFILE ifile, void *filestate) 320 { 321 int_ifile(ifile)->h_filestate = filestate; 322 } 323 324 public void set_altpipe(IFILE ifile, void *p) 325 { 326 int_ifile(ifile)->h_altpipe = p; 327 } 328 329 public void *get_altpipe(IFILE ifile) 330 { 331 return (int_ifile(ifile)->h_altpipe); 332 } 333 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 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