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