aboutsummaryrefslogtreecommitdiff
path: root/contrib/less/mark.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/less/mark.c')
-rw-r--r--contrib/less/mark.c458
1 files changed, 458 insertions, 0 deletions
diff --git a/contrib/less/mark.c b/contrib/less/mark.c
new file mode 100644
index 000000000000..cbb316f276e6
--- /dev/null
+++ b/contrib/less/mark.c
@@ -0,0 +1,458 @@
+/*
+ * Copyright (C) 1984-2021 Mark Nudelman
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Less License, as specified in the README file.
+ *
+ * For more information, see the README file.
+ */
+
+
+#include "less.h"
+#include "position.h"
+
+extern IFILE curr_ifile;
+extern int sc_height;
+extern int jump_sline;
+extern int perma_marks;
+
+/*
+ * A mark is an ifile (input file) plus a position within the file.
+ */
+struct mark
+{
+ /*
+ * Normally m_ifile != IFILE_NULL and m_filename == NULL.
+ * For restored marks we set m_filename instead of m_ifile
+ * because we don't want to create an ifile until the
+ * user explicitly requests the file (by name or mark).
+ */
+ char m_letter; /* Associated character */
+ IFILE m_ifile; /* Input file being marked */
+ char *m_filename; /* Name of the input file */
+ struct scrpos m_scrpos; /* Position of the mark */
+};
+
+/*
+ * The table of marks.
+ * Each mark is identified by a lowercase or uppercase letter.
+ * The final one is lmark, for the "last mark"; addressed by the apostrophe.
+ */
+#define NMARKS ((2*26)+2) /* a-z, A-Z, mousemark, lastmark */
+#define NUMARKS ((2*26)+1) /* user marks (not lastmark) */
+#define MOUSEMARK (NMARKS-2)
+#define LASTMARK (NMARKS-1)
+static struct mark marks[NMARKS];
+public int marks_modified = 0;
+
+
+/*
+ * Initialize a mark struct.
+ */
+ static void
+cmark(m, ifile, pos, ln)
+ struct mark *m;
+ IFILE ifile;
+ POSITION pos;
+ int ln;
+{
+ m->m_ifile = ifile;
+ m->m_scrpos.pos = pos;
+ m->m_scrpos.ln = ln;
+ m->m_filename = NULL;
+}
+
+/*
+ * Initialize the mark table to show no marks are set.
+ */
+ public void
+init_mark(VOID_PARAM)
+{
+ int i;
+
+ for (i = 0; i < NMARKS; i++)
+ {
+ char letter;
+ switch (i) {
+ case MOUSEMARK: letter = '#'; break;
+ case LASTMARK: letter = '\''; break;
+ default: letter = (i < 26) ? 'a'+i : 'A'+i-26; break;
+ }
+ marks[i].m_letter = letter;
+ cmark(&marks[i], NULL_IFILE, NULL_POSITION, -1);
+ }
+}
+
+/*
+ * Set m_ifile and clear m_filename.
+ */
+ static void
+mark_set_ifile(m, ifile)
+ struct mark *m;
+ IFILE ifile;
+{
+ m->m_ifile = ifile;
+ /* With m_ifile set, m_filename is no longer needed. */
+ free(m->m_filename);
+ m->m_filename = NULL;
+}
+
+/*
+ * Populate the m_ifile member of a mark struct from m_filename.
+ */
+ static void
+mark_get_ifile(m)
+ struct mark *m;
+{
+ if (m->m_ifile != NULL_IFILE)
+ return; /* m_ifile is already set */
+ mark_set_ifile(m, get_ifile(m->m_filename, prev_ifile(NULL_IFILE)));
+}
+
+/*
+ * Return the user mark struct identified by a character.
+ */
+ static struct mark *
+getumark(c)
+ int c;
+{
+ PARG parg;
+ if (c >= 'a' && c <= 'z')
+ return (&marks[c-'a']);
+ if (c >= 'A' && c <= 'Z')
+ return (&marks[c-'A'+26]);
+ if (c == '\'')
+ return (&marks[LASTMARK]);
+ if (c == '#')
+ return (&marks[MOUSEMARK]);
+ parg.p_char = (char) c;
+ error("Invalid mark letter %c", &parg);
+ return (NULL);
+}
+
+/*
+ * Get the mark structure identified by a character.
+ * The mark struct may either be in the mark table (user mark)
+ * or may be constructed on the fly for certain characters like ^, $.
+ */
+ static struct mark *
+getmark(c)
+ int c;
+{
+ struct mark *m;
+ static struct mark sm;
+
+ switch (c)
+ {
+ case '^':
+ /*
+ * Beginning of the current file.
+ */
+ m = &sm;
+ cmark(m, curr_ifile, ch_zero(), 0);
+ break;
+ case '$':
+ /*
+ * End of the current file.
+ */
+ if (ch_end_seek())
+ {
+ error("Cannot seek to end of file", NULL_PARG);
+ return (NULL);
+ }
+ m = &sm;
+ cmark(m, curr_ifile, ch_tell(), sc_height);
+ break;
+ case '.':
+ /*
+ * Current position in the current file.
+ */
+ m = &sm;
+ get_scrpos(&m->m_scrpos, TOP);
+ cmark(m, curr_ifile, m->m_scrpos.pos, m->m_scrpos.ln);
+ break;
+ case '\'':
+ /*
+ * The "last mark".
+ */
+ m = &marks[LASTMARK];
+ break;
+ default:
+ /*
+ * Must be a user-defined mark.
+ */
+ m = getumark(c);
+ if (m == NULL)
+ break;
+ if (m->m_scrpos.pos == NULL_POSITION)
+ {
+ error("Mark not set", NULL_PARG);
+ return (NULL);
+ }
+ break;
+ }
+ return (m);
+}
+
+/*
+ * Is a mark letter invalid?
+ */
+ public int
+badmark(c)
+ int c;
+{
+ return (getmark(c) == NULL);
+}
+
+/*
+ * Set a user-defined mark.
+ */
+ public void
+setmark(c, where)
+ int c;
+ int where;
+{
+ struct mark *m;
+ struct scrpos scrpos;
+
+ m = getumark(c);
+ if (m == NULL)
+ return;
+ get_scrpos(&scrpos, where);
+ if (scrpos.pos == NULL_POSITION)
+ {
+ bell();
+ return;
+ }
+ cmark(m, curr_ifile, scrpos.pos, scrpos.ln);
+ marks_modified = 1;
+}
+
+/*
+ * Clear a user-defined mark.
+ */
+ public void
+clrmark(c)
+ int c;
+{
+ struct mark *m;
+
+ m = getumark(c);
+ if (m == NULL)
+ return;
+ if (m->m_scrpos.pos == NULL_POSITION)
+ {
+ bell();
+ return;
+ }
+ m->m_scrpos.pos = NULL_POSITION;
+ marks_modified = 1;
+}
+
+/*
+ * Set lmark (the mark named by the apostrophe).
+ */
+ public void
+lastmark(VOID_PARAM)
+{
+ struct scrpos scrpos;
+
+ if (ch_getflags() & CH_HELPFILE)
+ return;
+ get_scrpos(&scrpos, TOP);
+ if (scrpos.pos == NULL_POSITION)
+ return;
+ cmark(&marks[LASTMARK], curr_ifile, scrpos.pos, scrpos.ln);
+ marks_modified = 1;
+}
+
+/*
+ * Go to a mark.
+ */
+ public void
+gomark(c)
+ int c;
+{
+ struct mark *m;
+ struct scrpos scrpos;
+
+ m = getmark(c);
+ if (m == NULL)
+ return;
+
+ /*
+ * If we're trying to go to the lastmark and
+ * it has not been set to anything yet,
+ * set it to the beginning of the current file.
+ * {{ Couldn't we instead set marks[LASTMARK] in edit()? }}
+ */
+ if (m == &marks[LASTMARK] && m->m_scrpos.pos == NULL_POSITION)
+ cmark(m, curr_ifile, ch_zero(), jump_sline);
+
+ mark_get_ifile(m);
+
+ /* Save scrpos; if it's LASTMARK it could change in edit_ifile. */
+ scrpos = m->m_scrpos;
+ if (m->m_ifile != curr_ifile)
+ {
+ /*
+ * Not in the current file; edit the correct file.
+ */
+ if (edit_ifile(m->m_ifile))
+ return;
+ }
+
+ jump_loc(scrpos.pos, scrpos.ln);
+}
+
+/*
+ * Return the position associated with a given mark letter.
+ *
+ * We don't return which screen line the position
+ * is associated with, but this doesn't matter much,
+ * because it's always the first non-blank line on the screen.
+ */
+ public POSITION
+markpos(c)
+ int c;
+{
+ struct mark *m;
+
+ m = getmark(c);
+ if (m == NULL)
+ return (NULL_POSITION);
+
+ if (m->m_ifile != curr_ifile)
+ {
+ error("Mark not in current file", NULL_PARG);
+ return (NULL_POSITION);
+ }
+ return (m->m_scrpos.pos);
+}
+
+/*
+ * Return the mark associated with a given position, if any.
+ */
+ public char
+posmark(pos)
+ POSITION pos;
+{
+ int i;
+
+ /* Only user marks */
+ for (i = 0; i < NUMARKS; i++)
+ {
+ if (marks[i].m_ifile == curr_ifile && marks[i].m_scrpos.pos == pos)
+ {
+ if (i < 26) return 'a' + i;
+ if (i < 26*2) return 'A' + (i - 26);
+ return '#';
+ }
+ }
+ return 0;
+}
+
+/*
+ * Clear the marks associated with a specified ifile.
+ */
+ public void
+unmark(ifile)
+ IFILE ifile;
+{
+ int i;
+
+ for (i = 0; i < NMARKS; i++)
+ if (marks[i].m_ifile == ifile)
+ marks[i].m_scrpos.pos = NULL_POSITION;
+}
+
+/*
+ * Check if any marks refer to a specified ifile vi m_filename
+ * rather than m_ifile.
+ */
+ public void
+mark_check_ifile(ifile)
+ IFILE ifile;
+{
+ int i;
+ char *filename = get_real_filename(ifile);
+
+ for (i = 0; i < NMARKS; i++)
+ {
+ struct mark *m = &marks[i];
+ char *mark_filename = m->m_filename;
+ if (mark_filename != NULL)
+ {
+ mark_filename = lrealpath(mark_filename);
+ if (strcmp(filename, mark_filename) == 0)
+ mark_set_ifile(m, ifile);
+ free(mark_filename);
+ }
+ }
+}
+
+#if CMD_HISTORY
+
+/*
+ * Save marks to history file.
+ */
+ public void
+save_marks(fout, hdr)
+ FILE *fout;
+ char *hdr;
+{
+ int i;
+
+ if (!perma_marks)
+ return;
+
+ fprintf(fout, "%s\n", hdr);
+ for (i = 0; i < NMARKS; i++)
+ {
+ char *filename;
+ struct mark *m = &marks[i];
+ char pos_str[INT_STRLEN_BOUND(m->m_scrpos.pos) + 2];
+ if (m->m_scrpos.pos == NULL_POSITION)
+ continue;
+ postoa(m->m_scrpos.pos, pos_str);
+ filename = m->m_filename;
+ if (filename == NULL)
+ filename = get_real_filename(m->m_ifile);
+ if (strcmp(filename, "-") != 0)
+ fprintf(fout, "m %c %d %s %s\n",
+ m->m_letter, m->m_scrpos.ln, pos_str, filename);
+ }
+}
+
+/*
+ * Restore one mark from the history file.
+ */
+ public void
+restore_mark(line)
+ char *line;
+{
+ struct mark *m;
+ int ln;
+ POSITION pos;
+
+#define skip_whitespace while (*line == ' ') line++
+ if (*line++ != 'm')
+ return;
+ skip_whitespace;
+ m = getumark(*line++);
+ if (m == NULL)
+ return;
+ skip_whitespace;
+ ln = lstrtoi(line, &line);
+ if (ln < 1)
+ ln = 1;
+ if (ln > sc_height)
+ ln = sc_height;
+ skip_whitespace;
+ pos = lstrtopos(line, &line);
+ skip_whitespace;
+ cmark(m, NULL_IFILE, pos, ln);
+ m->m_filename = save(line);
+}
+
+#endif /* CMD_HISTORY */