1 /* 2 * Copyright (C) 1984-2017 Mark Nudelman 3 * 4 * You may distribute under the terms of either the GNU General Public 5 * License or the Less License, as specified in the README file. 6 * 7 * For more information, see the README file. 8 */ 9 10 11 #include "less.h" 12 #include "position.h" 13 14 extern IFILE curr_ifile; 15 extern int sc_height; 16 extern int jump_sline; 17 18 /* 19 * The table of marks. 20 * Each mark is identified by a lowercase or uppercase letter. 21 * The final one is lmark, for the "last mark"; addressed by the apostrophe. 22 */ 23 #define NMARKS ((2*26)+1) /* a-z, A-Z, lastmark */ 24 #define LASTMARK (NMARKS-1) 25 static struct mark marks[NMARKS]; 26 27 /* 28 * Initialize the mark table to show no marks are set. 29 */ 30 public void 31 init_mark() 32 { 33 int i; 34 35 for (i = 0; i < NMARKS; i++) 36 marks[i].m_scrpos.pos = NULL_POSITION; 37 } 38 39 /* 40 * See if a mark letter is valid (between a and z). 41 */ 42 static struct mark * 43 getumark(c) 44 int c; 45 { 46 if (c >= 'a' && c <= 'z') 47 return (&marks[c-'a']); 48 49 if (c >= 'A' && c <= 'Z') 50 return (&marks[c-'A'+26]); 51 52 error("Invalid mark letter", NULL_PARG); 53 return (NULL); 54 } 55 56 /* 57 * Get the mark structure identified by a character. 58 * The mark struct may come either from the mark table 59 * or may be constructed on the fly for certain characters like ^, $. 60 */ 61 static struct mark * 62 getmark(c) 63 int c; 64 { 65 struct mark *m; 66 static struct mark sm; 67 68 switch (c) 69 { 70 case '^': 71 /* 72 * Beginning of the current file. 73 */ 74 m = &sm; 75 m->m_scrpos.pos = ch_zero(); 76 m->m_scrpos.ln = 0; 77 m->m_ifile = curr_ifile; 78 break; 79 case '$': 80 /* 81 * End of the current file. 82 */ 83 if (ch_end_seek()) 84 { 85 error("Cannot seek to end of file", NULL_PARG); 86 return (NULL); 87 } 88 m = &sm; 89 m->m_scrpos.pos = ch_tell(); 90 m->m_scrpos.ln = sc_height; 91 m->m_ifile = curr_ifile; 92 break; 93 case '.': 94 /* 95 * Current position in the current file. 96 */ 97 m = &sm; 98 get_scrpos(&m->m_scrpos, TOP); 99 m->m_ifile = curr_ifile; 100 break; 101 case '\'': 102 /* 103 * The "last mark". 104 */ 105 m = &marks[LASTMARK]; 106 break; 107 default: 108 /* 109 * Must be a user-defined mark. 110 */ 111 m = getumark(c); 112 if (m == NULL) 113 break; 114 if (m->m_scrpos.pos == NULL_POSITION) 115 { 116 error("Mark not set", NULL_PARG); 117 return (NULL); 118 } 119 break; 120 } 121 return (m); 122 } 123 124 /* 125 * Is a mark letter is invalid? 126 */ 127 public int 128 badmark(c) 129 int c; 130 { 131 return (getmark(c) == NULL); 132 } 133 134 /* 135 * Set a user-defined mark. 136 */ 137 public void 138 setmark(c, where) 139 int c; 140 int where; 141 { 142 struct mark *m; 143 struct scrpos scrpos; 144 145 m = getumark(c); 146 if (m == NULL) 147 return; 148 get_scrpos(&scrpos, where); 149 m->m_scrpos = scrpos; 150 m->m_ifile = curr_ifile; 151 } 152 153 /* 154 * Clear a user-defined mark. 155 */ 156 public void 157 clrmark(c) 158 int c; 159 { 160 struct mark *m; 161 162 m = getumark(c); 163 if (m == NULL) 164 return; 165 m->m_scrpos.pos = NULL_POSITION; 166 } 167 168 /* 169 * Set lmark (the mark named by the apostrophe). 170 */ 171 public void 172 lastmark() 173 { 174 struct scrpos scrpos; 175 176 if (ch_getflags() & CH_HELPFILE) 177 return; 178 get_scrpos(&scrpos, TOP); 179 if (scrpos.pos == NULL_POSITION) 180 return; 181 marks[LASTMARK].m_scrpos = scrpos; 182 marks[LASTMARK].m_ifile = curr_ifile; 183 } 184 185 /* 186 * Go to a mark. 187 */ 188 public void 189 gomark(c) 190 int c; 191 { 192 struct mark *m; 193 struct scrpos scrpos; 194 195 m = getmark(c); 196 if (m == NULL) 197 return; 198 199 /* 200 * If we're trying to go to the lastmark and 201 * it has not been set to anything yet, 202 * set it to the beginning of the current file. 203 */ 204 if (m == &marks[LASTMARK] && m->m_scrpos.pos == NULL_POSITION) 205 { 206 m->m_ifile = curr_ifile; 207 m->m_scrpos.pos = ch_zero(); 208 m->m_scrpos.ln = jump_sline; 209 } 210 211 /* 212 * If we're using lmark, we must save the screen position now, 213 * because if we call edit_ifile() below, lmark will change. 214 * (We save the screen position even if we're not using lmark.) 215 */ 216 scrpos = m->m_scrpos; 217 if (m->m_ifile != curr_ifile) 218 { 219 /* 220 * Not in the current file; edit the correct file. 221 */ 222 if (edit_ifile(m->m_ifile)) 223 return; 224 } 225 226 jump_loc(scrpos.pos, scrpos.ln); 227 } 228 229 /* 230 * Return the position associated with a given mark letter. 231 * 232 * We don't return which screen line the position 233 * is associated with, but this doesn't matter much, 234 * because it's always the first non-blank line on the screen. 235 */ 236 public POSITION 237 markpos(c) 238 int c; 239 { 240 struct mark *m; 241 242 m = getmark(c); 243 if (m == NULL) 244 return (NULL_POSITION); 245 246 if (m->m_ifile != curr_ifile) 247 { 248 error("Mark not in current file", NULL_PARG); 249 return (NULL_POSITION); 250 } 251 return (m->m_scrpos.pos); 252 } 253 254 /* 255 * Return the mark associated with a given position, if any. 256 */ 257 public char 258 posmark(pos) 259 POSITION pos; 260 { 261 int i; 262 263 /* Only lower case and upper case letters */ 264 for (i = 0; i < 26*2; i++) 265 { 266 if (marks[i].m_ifile == curr_ifile && marks[i].m_scrpos.pos == pos) 267 { 268 if (i < 26) return 'a' + i; 269 return 'A' + i - 26; 270 } 271 } 272 return 0; 273 } 274 275 /* 276 * Clear the marks associated with a specified ifile. 277 */ 278 public void 279 unmark(ifile) 280 IFILE ifile; 281 { 282 int i; 283 284 for (i = 0; i < NMARKS; i++) 285 if (marks[i].m_ifile == ifile) 286 marks[i].m_scrpos.pos = NULL_POSITION; 287 } 288