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