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