1a5f0fb15SPaul Saab /* 2*c77c4889SXin LI * Copyright (C) 1984-2024 Mark Nudelman 3a5f0fb15SPaul Saab * 4a5f0fb15SPaul Saab * You may distribute under the terms of either the GNU General Public 5a5f0fb15SPaul Saab * License or the Less License, as specified in the README file. 6a5f0fb15SPaul Saab * 796e55cc7SXin LI * For more information, see the README file. 8a5f0fb15SPaul Saab */ 9a5f0fb15SPaul Saab 10a5f0fb15SPaul Saab 11a5f0fb15SPaul Saab #include "less.h" 12b2ea2440SXin LI #include "position.h" 13a5f0fb15SPaul Saab 14a5f0fb15SPaul Saab extern IFILE curr_ifile; 15a5f0fb15SPaul Saab extern int sc_height; 16a5f0fb15SPaul Saab extern int jump_sline; 17b7780dbeSXin LI extern int perma_marks; 18b7780dbeSXin LI 19b7780dbeSXin LI /* 20b7780dbeSXin LI * A mark is an ifile (input file) plus a position within the file. 21b7780dbeSXin LI */ 22b7780dbeSXin LI struct mark 23b7780dbeSXin LI { 24b7780dbeSXin LI /* 25b7780dbeSXin LI * Normally m_ifile != IFILE_NULL and m_filename == NULL. 26b7780dbeSXin LI * For restored marks we set m_filename instead of m_ifile 27b7780dbeSXin LI * because we don't want to create an ifile until the 28b7780dbeSXin LI * user explicitly requests the file (by name or mark). 29b7780dbeSXin LI */ 30b7780dbeSXin LI char m_letter; /* Associated character */ 31b7780dbeSXin LI IFILE m_ifile; /* Input file being marked */ 32b7780dbeSXin LI char *m_filename; /* Name of the input file */ 33b7780dbeSXin LI struct scrpos m_scrpos; /* Position of the mark */ 34b7780dbeSXin LI }; 35a5f0fb15SPaul Saab 36a5f0fb15SPaul Saab /* 37a5f0fb15SPaul Saab * The table of marks. 38a5f0fb15SPaul Saab * Each mark is identified by a lowercase or uppercase letter. 39a5f0fb15SPaul Saab * The final one is lmark, for the "last mark"; addressed by the apostrophe. 40a5f0fb15SPaul Saab */ 41b7780dbeSXin LI #define NMARKS ((2*26)+2) /* a-z, A-Z, mousemark, lastmark */ 42b7780dbeSXin LI #define NUMARKS ((2*26)+1) /* user marks (not lastmark) */ 43b7780dbeSXin LI #define MOUSEMARK (NMARKS-2) 44a5f0fb15SPaul Saab #define LASTMARK (NMARKS-1) 45a5f0fb15SPaul Saab static struct mark marks[NMARKS]; 46b7780dbeSXin LI public int marks_modified = 0; 47b7780dbeSXin LI 48b7780dbeSXin LI 49b7780dbeSXin LI /* 50b7780dbeSXin LI * Initialize a mark struct. 51b7780dbeSXin LI */ 52d713e089SXin LI static void cmark(struct mark *m, IFILE ifile, POSITION pos, int ln) 53b7780dbeSXin LI { 54b7780dbeSXin LI m->m_ifile = ifile; 55b7780dbeSXin LI m->m_scrpos.pos = pos; 56b7780dbeSXin LI m->m_scrpos.ln = ln; 5795270f73SXin LI if (m->m_filename != NULL) 5895270f73SXin LI /* Normally should not happen but a corrupt lesshst file can do it. */ 5995270f73SXin LI free(m->m_filename); 60b7780dbeSXin LI m->m_filename = NULL; 61b7780dbeSXin LI } 62a5f0fb15SPaul Saab 63a5f0fb15SPaul Saab /* 64a5f0fb15SPaul Saab * Initialize the mark table to show no marks are set. 65a5f0fb15SPaul Saab */ 66d713e089SXin LI public void init_mark(void) 67a5f0fb15SPaul Saab { 68a5f0fb15SPaul Saab int i; 69a5f0fb15SPaul Saab 70a5f0fb15SPaul Saab for (i = 0; i < NMARKS; i++) 71b7780dbeSXin LI { 72b7780dbeSXin LI char letter; 73b7780dbeSXin LI switch (i) { 74b7780dbeSXin LI case MOUSEMARK: letter = '#'; break; 75b7780dbeSXin LI case LASTMARK: letter = '\''; break; 76*c77c4889SXin LI default: letter = (char) ((i < 26) ? 'a'+i : 'A'+i-26); break; 77b7780dbeSXin LI } 78b7780dbeSXin LI marks[i].m_letter = letter; 79b7780dbeSXin LI cmark(&marks[i], NULL_IFILE, NULL_POSITION, -1); 80b7780dbeSXin LI } 81a5f0fb15SPaul Saab } 82a5f0fb15SPaul Saab 83a5f0fb15SPaul Saab /* 84b7780dbeSXin LI * Set m_ifile and clear m_filename. 85b7780dbeSXin LI */ 86d713e089SXin LI static void mark_set_ifile(struct mark *m, IFILE ifile) 87b7780dbeSXin LI { 88b7780dbeSXin LI m->m_ifile = ifile; 89b7780dbeSXin LI /* With m_ifile set, m_filename is no longer needed. */ 90b7780dbeSXin LI free(m->m_filename); 91b7780dbeSXin LI m->m_filename = NULL; 92b7780dbeSXin LI } 93b7780dbeSXin LI 94b7780dbeSXin LI /* 95b7780dbeSXin LI * Populate the m_ifile member of a mark struct from m_filename. 96b7780dbeSXin LI */ 97d713e089SXin LI static void mark_get_ifile(struct mark *m) 98b7780dbeSXin LI { 99b7780dbeSXin LI if (m->m_ifile != NULL_IFILE) 100b7780dbeSXin LI return; /* m_ifile is already set */ 101b7780dbeSXin LI mark_set_ifile(m, get_ifile(m->m_filename, prev_ifile(NULL_IFILE))); 102b7780dbeSXin LI } 103b7780dbeSXin LI 104b7780dbeSXin LI /* 105b7780dbeSXin LI * Return the user mark struct identified by a character. 106a5f0fb15SPaul Saab */ 107*c77c4889SXin LI static struct mark * getumark(char c) 108a5f0fb15SPaul Saab { 1092235c7feSXin LI PARG parg; 110a5f0fb15SPaul Saab if (c >= 'a' && c <= 'z') 111a5f0fb15SPaul Saab return (&marks[c-'a']); 112a5f0fb15SPaul Saab if (c >= 'A' && c <= 'Z') 113a5f0fb15SPaul Saab return (&marks[c-'A'+26]); 1142235c7feSXin LI if (c == '\'') 1152235c7feSXin LI return (&marks[LASTMARK]); 116b7780dbeSXin LI if (c == '#') 117b7780dbeSXin LI return (&marks[MOUSEMARK]); 1182235c7feSXin LI parg.p_char = (char) c; 1192235c7feSXin LI error("Invalid mark letter %c", &parg); 120a5f0fb15SPaul Saab return (NULL); 121a5f0fb15SPaul Saab } 122a5f0fb15SPaul Saab 123a5f0fb15SPaul Saab /* 124a5f0fb15SPaul Saab * Get the mark structure identified by a character. 125b7780dbeSXin LI * The mark struct may either be in the mark table (user mark) 126a5f0fb15SPaul Saab * or may be constructed on the fly for certain characters like ^, $. 127a5f0fb15SPaul Saab */ 128*c77c4889SXin LI static struct mark * getmark(char c) 129a5f0fb15SPaul Saab { 1301ea31627SRobert Watson struct mark *m; 131a5f0fb15SPaul Saab static struct mark sm; 132a5f0fb15SPaul Saab 133a5f0fb15SPaul Saab switch (c) 134a5f0fb15SPaul Saab { 135a5f0fb15SPaul Saab case '^': 136a5f0fb15SPaul Saab /* 137a5f0fb15SPaul Saab * Beginning of the current file. 138a5f0fb15SPaul Saab */ 139a5f0fb15SPaul Saab m = &sm; 140b7780dbeSXin LI cmark(m, curr_ifile, ch_zero(), 0); 141a5f0fb15SPaul Saab break; 142a5f0fb15SPaul Saab case '$': 143a5f0fb15SPaul Saab /* 144a5f0fb15SPaul Saab * End of the current file. 145a5f0fb15SPaul Saab */ 146a5f0fb15SPaul Saab if (ch_end_seek()) 147a5f0fb15SPaul Saab { 148a5f0fb15SPaul Saab error("Cannot seek to end of file", NULL_PARG); 149a5f0fb15SPaul Saab return (NULL); 150a5f0fb15SPaul Saab } 151a5f0fb15SPaul Saab m = &sm; 152b7780dbeSXin LI cmark(m, curr_ifile, ch_tell(), sc_height); 153a5f0fb15SPaul Saab break; 154a5f0fb15SPaul Saab case '.': 155a5f0fb15SPaul Saab /* 156a5f0fb15SPaul Saab * Current position in the current file. 157a5f0fb15SPaul Saab */ 158a5f0fb15SPaul Saab m = &sm; 159b2ea2440SXin LI get_scrpos(&m->m_scrpos, TOP); 160b7780dbeSXin LI cmark(m, curr_ifile, m->m_scrpos.pos, m->m_scrpos.ln); 161a5f0fb15SPaul Saab break; 162a5f0fb15SPaul Saab case '\'': 163a5f0fb15SPaul Saab /* 164a5f0fb15SPaul Saab * The "last mark". 165a5f0fb15SPaul Saab */ 166a5f0fb15SPaul Saab m = &marks[LASTMARK]; 167a5f0fb15SPaul Saab break; 168a5f0fb15SPaul Saab default: 169a5f0fb15SPaul Saab /* 170a5f0fb15SPaul Saab * Must be a user-defined mark. 171a5f0fb15SPaul Saab */ 172a5f0fb15SPaul Saab m = getumark(c); 173a5f0fb15SPaul Saab if (m == NULL) 174a5f0fb15SPaul Saab break; 175a5f0fb15SPaul Saab if (m->m_scrpos.pos == NULL_POSITION) 176a5f0fb15SPaul Saab { 177a5f0fb15SPaul Saab error("Mark not set", NULL_PARG); 178a5f0fb15SPaul Saab return (NULL); 179a5f0fb15SPaul Saab } 180a5f0fb15SPaul Saab break; 181a5f0fb15SPaul Saab } 182a5f0fb15SPaul Saab return (m); 183a5f0fb15SPaul Saab } 184a5f0fb15SPaul Saab 185a5f0fb15SPaul Saab /* 186b7780dbeSXin LI * Is a mark letter invalid? 187a5f0fb15SPaul Saab */ 188*c77c4889SXin LI public int badmark(char c) 189a5f0fb15SPaul Saab { 190a5f0fb15SPaul Saab return (getmark(c) == NULL); 191a5f0fb15SPaul Saab } 192a5f0fb15SPaul Saab 193a5f0fb15SPaul Saab /* 194a5f0fb15SPaul Saab * Set a user-defined mark. 195a5f0fb15SPaul Saab */ 196*c77c4889SXin LI public void setmark(char c, int where) 197a5f0fb15SPaul Saab { 1981ea31627SRobert Watson struct mark *m; 199a5f0fb15SPaul Saab struct scrpos scrpos; 200a5f0fb15SPaul Saab 201a5f0fb15SPaul Saab m = getumark(c); 202a5f0fb15SPaul Saab if (m == NULL) 203a5f0fb15SPaul Saab return; 204b2ea2440SXin LI get_scrpos(&scrpos, where); 205b7780dbeSXin LI if (scrpos.pos == NULL_POSITION) 206b7780dbeSXin LI { 207b7780dbeSXin LI bell(); 208b7780dbeSXin LI return; 209b7780dbeSXin LI } 210b7780dbeSXin LI cmark(m, curr_ifile, scrpos.pos, scrpos.ln); 211b7780dbeSXin LI marks_modified = 1; 212a5f0fb15SPaul Saab } 213a5f0fb15SPaul Saab 214a5f0fb15SPaul Saab /* 215b2ea2440SXin LI * Clear a user-defined mark. 216b2ea2440SXin LI */ 217*c77c4889SXin LI public void clrmark(char c) 218b2ea2440SXin LI { 219b2ea2440SXin LI struct mark *m; 220b2ea2440SXin LI 221b2ea2440SXin LI m = getumark(c); 222b2ea2440SXin LI if (m == NULL) 223b2ea2440SXin LI return; 224b7780dbeSXin LI if (m->m_scrpos.pos == NULL_POSITION) 225b7780dbeSXin LI { 226b7780dbeSXin LI bell(); 227b7780dbeSXin LI return; 228b7780dbeSXin LI } 229b2ea2440SXin LI m->m_scrpos.pos = NULL_POSITION; 230b7780dbeSXin LI marks_modified = 1; 231b2ea2440SXin LI } 232b2ea2440SXin LI 233b2ea2440SXin LI /* 234a5f0fb15SPaul Saab * Set lmark (the mark named by the apostrophe). 235a5f0fb15SPaul Saab */ 236d713e089SXin LI public void lastmark(void) 237a5f0fb15SPaul Saab { 238a5f0fb15SPaul Saab struct scrpos scrpos; 239a5f0fb15SPaul Saab 240a5f0fb15SPaul Saab if (ch_getflags() & CH_HELPFILE) 241a5f0fb15SPaul Saab return; 242b2ea2440SXin LI get_scrpos(&scrpos, TOP); 243a5f0fb15SPaul Saab if (scrpos.pos == NULL_POSITION) 244a5f0fb15SPaul Saab return; 245b7780dbeSXin LI cmark(&marks[LASTMARK], curr_ifile, scrpos.pos, scrpos.ln); 2462235c7feSXin LI marks_modified = 1; 247a5f0fb15SPaul Saab } 248a5f0fb15SPaul Saab 249a5f0fb15SPaul Saab /* 250a5f0fb15SPaul Saab * Go to a mark. 251a5f0fb15SPaul Saab */ 252*c77c4889SXin LI public void gomark(char c) 253a5f0fb15SPaul Saab { 2541ea31627SRobert Watson struct mark *m; 255a5f0fb15SPaul Saab struct scrpos scrpos; 256a5f0fb15SPaul Saab 257a5f0fb15SPaul Saab m = getmark(c); 258a5f0fb15SPaul Saab if (m == NULL) 259a5f0fb15SPaul Saab return; 260a5f0fb15SPaul Saab 261a5f0fb15SPaul Saab /* 262a5f0fb15SPaul Saab * If we're trying to go to the lastmark and 263a5f0fb15SPaul Saab * it has not been set to anything yet, 264a5f0fb15SPaul Saab * set it to the beginning of the current file. 265b7780dbeSXin LI * {{ Couldn't we instead set marks[LASTMARK] in edit()? }} 266a5f0fb15SPaul Saab */ 267a5f0fb15SPaul Saab if (m == &marks[LASTMARK] && m->m_scrpos.pos == NULL_POSITION) 268b7780dbeSXin LI cmark(m, curr_ifile, ch_zero(), jump_sline); 269a5f0fb15SPaul Saab 270b7780dbeSXin LI mark_get_ifile(m); 271b7780dbeSXin LI 272b7780dbeSXin LI /* Save scrpos; if it's LASTMARK it could change in edit_ifile. */ 273a5f0fb15SPaul Saab scrpos = m->m_scrpos; 274a5f0fb15SPaul Saab if (m->m_ifile != curr_ifile) 275a5f0fb15SPaul Saab { 276a5f0fb15SPaul Saab /* 277a5f0fb15SPaul Saab * Not in the current file; edit the correct file. 278a5f0fb15SPaul Saab */ 279a5f0fb15SPaul Saab if (edit_ifile(m->m_ifile)) 280a5f0fb15SPaul Saab return; 281a5f0fb15SPaul Saab } 282a5f0fb15SPaul Saab 283a5f0fb15SPaul Saab jump_loc(scrpos.pos, scrpos.ln); 284a5f0fb15SPaul Saab } 285a5f0fb15SPaul Saab 286a5f0fb15SPaul Saab /* 287a5f0fb15SPaul Saab * Return the position associated with a given mark letter. 288a5f0fb15SPaul Saab * 289a5f0fb15SPaul Saab * We don't return which screen line the position 290a5f0fb15SPaul Saab * is associated with, but this doesn't matter much, 291a5f0fb15SPaul Saab * because it's always the first non-blank line on the screen. 292a5f0fb15SPaul Saab */ 293*c77c4889SXin LI public POSITION markpos(char c) 294a5f0fb15SPaul Saab { 2951ea31627SRobert Watson struct mark *m; 296a5f0fb15SPaul Saab 297a5f0fb15SPaul Saab m = getmark(c); 298a5f0fb15SPaul Saab if (m == NULL) 299a5f0fb15SPaul Saab return (NULL_POSITION); 300a5f0fb15SPaul Saab 301a5f0fb15SPaul Saab if (m->m_ifile != curr_ifile) 302a5f0fb15SPaul Saab { 303a5f0fb15SPaul Saab error("Mark not in current file", NULL_PARG); 304a5f0fb15SPaul Saab return (NULL_POSITION); 305a5f0fb15SPaul Saab } 306a5f0fb15SPaul Saab return (m->m_scrpos.pos); 307a5f0fb15SPaul Saab } 308a5f0fb15SPaul Saab 309a5f0fb15SPaul Saab /* 310b2ea2440SXin LI * Return the mark associated with a given position, if any. 311b2ea2440SXin LI */ 312d713e089SXin LI public char posmark(POSITION pos) 313b2ea2440SXin LI { 314*c77c4889SXin LI unsigned char i; 315b2ea2440SXin LI 316b7780dbeSXin LI /* Only user marks */ 317b7780dbeSXin LI for (i = 0; i < NUMARKS; i++) 318b2ea2440SXin LI { 319b2ea2440SXin LI if (marks[i].m_ifile == curr_ifile && marks[i].m_scrpos.pos == pos) 320b2ea2440SXin LI { 321*c77c4889SXin LI if (i < 26) return (char) ('a' + i); 322*c77c4889SXin LI if (i < 26*2) return (char) ('A' + (i - 26)); 323b7780dbeSXin LI return '#'; 324b2ea2440SXin LI } 325b2ea2440SXin LI } 326b2ea2440SXin LI return 0; 327b2ea2440SXin LI } 328b2ea2440SXin LI 329b2ea2440SXin LI /* 330a5f0fb15SPaul Saab * Clear the marks associated with a specified ifile. 331a5f0fb15SPaul Saab */ 332d713e089SXin LI public void unmark(IFILE ifile) 333a5f0fb15SPaul Saab { 334a5f0fb15SPaul Saab int i; 335a5f0fb15SPaul Saab 336a5f0fb15SPaul Saab for (i = 0; i < NMARKS; i++) 337a5f0fb15SPaul Saab if (marks[i].m_ifile == ifile) 338a5f0fb15SPaul Saab marks[i].m_scrpos.pos = NULL_POSITION; 339a5f0fb15SPaul Saab } 340b7780dbeSXin LI 341b7780dbeSXin LI /* 342b7780dbeSXin LI * Check if any marks refer to a specified ifile vi m_filename 343b7780dbeSXin LI * rather than m_ifile. 344b7780dbeSXin LI */ 345d713e089SXin LI public void mark_check_ifile(IFILE ifile) 346b7780dbeSXin LI { 347b7780dbeSXin LI int i; 348*c77c4889SXin LI constant char *filename = get_real_filename(ifile); 349b7780dbeSXin LI 350b7780dbeSXin LI for (i = 0; i < NMARKS; i++) 351b7780dbeSXin LI { 352b7780dbeSXin LI struct mark *m = &marks[i]; 353b7780dbeSXin LI char *mark_filename = m->m_filename; 354b7780dbeSXin LI if (mark_filename != NULL) 355b7780dbeSXin LI { 356b7780dbeSXin LI mark_filename = lrealpath(mark_filename); 357b7780dbeSXin LI if (strcmp(filename, mark_filename) == 0) 358b7780dbeSXin LI mark_set_ifile(m, ifile); 359b7780dbeSXin LI free(mark_filename); 360b7780dbeSXin LI } 361b7780dbeSXin LI } 362b7780dbeSXin LI } 363b7780dbeSXin LI 364b7780dbeSXin LI #if CMD_HISTORY 365b7780dbeSXin LI 366b7780dbeSXin LI /* 367b7780dbeSXin LI * Save marks to history file. 368b7780dbeSXin LI */ 369*c77c4889SXin LI public void save_marks(FILE *fout, constant char *hdr) 370b7780dbeSXin LI { 371b7780dbeSXin LI int i; 372b7780dbeSXin LI 373b7780dbeSXin LI if (!perma_marks) 374b7780dbeSXin LI return; 375b7780dbeSXin LI 376b7780dbeSXin LI fprintf(fout, "%s\n", hdr); 3772235c7feSXin LI for (i = 0; i < NMARKS; i++) 378b7780dbeSXin LI { 379*c77c4889SXin LI constant char *filename; 380b7780dbeSXin LI struct mark *m = &marks[i]; 381b7780dbeSXin LI char pos_str[INT_STRLEN_BOUND(m->m_scrpos.pos) + 2]; 382b7780dbeSXin LI if (m->m_scrpos.pos == NULL_POSITION) 383b7780dbeSXin LI continue; 384d713e089SXin LI postoa(m->m_scrpos.pos, pos_str, 10); 385b7780dbeSXin LI filename = m->m_filename; 386b7780dbeSXin LI if (filename == NULL) 3872235c7feSXin LI filename = get_real_filename(m->m_ifile); 388b7780dbeSXin LI if (strcmp(filename, "-") != 0) 389b7780dbeSXin LI fprintf(fout, "m %c %d %s %s\n", 390b7780dbeSXin LI m->m_letter, m->m_scrpos.ln, pos_str, filename); 391b7780dbeSXin LI } 392b7780dbeSXin LI } 393b7780dbeSXin LI 394b7780dbeSXin LI /* 395b7780dbeSXin LI * Restore one mark from the history file. 396b7780dbeSXin LI */ 397*c77c4889SXin LI public void restore_mark(constant char *line) 398b7780dbeSXin LI { 399b7780dbeSXin LI struct mark *m; 400b7780dbeSXin LI int ln; 401b7780dbeSXin LI POSITION pos; 402b7780dbeSXin LI 403b7780dbeSXin LI #define skip_whitespace while (*line == ' ') line++ 404b7780dbeSXin LI if (*line++ != 'm') 405b7780dbeSXin LI return; 406b7780dbeSXin LI skip_whitespace; 407b7780dbeSXin LI m = getumark(*line++); 408b7780dbeSXin LI if (m == NULL) 409b7780dbeSXin LI return; 410b7780dbeSXin LI skip_whitespace; 411*c77c4889SXin LI ln = lstrtoic(line, &line, 10); 412d713e089SXin LI if (ln < 0) 413d713e089SXin LI return; 414b7780dbeSXin LI if (ln < 1) 415b7780dbeSXin LI ln = 1; 416b7780dbeSXin LI if (ln > sc_height) 417b7780dbeSXin LI ln = sc_height; 418b7780dbeSXin LI skip_whitespace; 419*c77c4889SXin LI pos = lstrtoposc(line, &line, 10); 420d713e089SXin LI if (pos < 0) 421d713e089SXin LI return; 422b7780dbeSXin LI skip_whitespace; 423b7780dbeSXin LI cmark(m, NULL_IFILE, pos, ln); 424b7780dbeSXin LI m->m_filename = save(line); 425b7780dbeSXin LI } 426b7780dbeSXin LI 427b7780dbeSXin LI #endif /* CMD_HISTORY */ 428