/*
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static char sccsid[] = "@(#)prim.c 8.1 (Berkeley) 6/6/93";
#endif /* not lint */
#ifndef lint
static const char rcsid[] =
"$FreeBSD: src/usr.bin/more/prim.c,v 1.10 2000/05/14 03:30:59 hoek Exp $";
#endif /* not lint */
/*
* Bookmarking commands.
*/
#include <sys/param.h>
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <regex.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "less.h"
extern int sc_width, sc_height;
extern int horiz_off;
extern int wraplines;
extern char *line;
extern int retain_below;
typedef char * ch_file;
/*
* A mark is simply a viewing position of a file. This structure is considered
* opaque outside of dosetmark(), marks_bookhints(), gomark(), and initmark().
*
* An initialised mark pointing at nowhere has hint == file == NULL and
* pos == 0.
*
* An initialised mark pointing at somwhere has all valid fields and possibly
* a NULL hint. The hint must be free'd before changing it.
*/
struct mark {
int horiz_off; /* value of horiz_off */
int wraplines; /* value of wraplines */
off_t pos; /* for jump_loc() */
ch_file file; /* file into which mark points */
char *hint; /* hint for human user of the mark's destination */
int linenum; /* optional line number to suplement hint */
};
/*
* The table of bookmarks.
*/
#define NMARKS (27) /* 26 for a-z plus one for quote */
#define LASTMARK (NMARKS-1) /* For quote */
static struct mark marks[NMARKS];
/*
* Internal function prototypes.
*/
static void dosetmark(struct mark *);
static void initmark(struct mark *);
static int badmark(int);
/*
* Initialize the mark table to show no marks are set.
*/
void
marks_initmarks(void)
{
int i;
for (i = 0; i < NMARKS; i++)
initmark(&marks[i]);
}
/*
* See if a mark letter is valid (between a and z).
*/
static int
badmark(c)
int c;
{
if (c < 'a' || c > 'z') {
error("Choose a letter between 'a' and 'z'");
return (1);
}
return (0);
}
/*
* Set a bookmark.
*/
setmark(c)
int c; /* user identifier for bookmark to set */
{
if (badmark(c)) return;
dosetmark(&marks[c - 'a']);
}
/*
* This function should be called whenever the file position we are viewing
* is changed by a significant amount. The function will record the current
* position and remember it for the user.
*/
lastmark()
{
dosetmark(&marks[LASTMARK]);
}
/*
* Initialise mark pointed at by mark_p to an empty mark.
*/
static void
initmark (mark_p)
struct mark * mark_p; /* mark to initialize */
{
mark_p->pos = NULL_POSITION;
mark_p->file = NULL;
mark_p->hint = NULL;
}
/*
* This function sets the mark pointed at by mark_p to the current viewing
* position.
*/
static void
dosetmark(mark_p)
struct mark * mark_p; /* bookmark to set to the current position */
{
extern char *current_file;
extern char *line;
off_t npos, t, maxpos;
int maxlen;
int i;
mark_p->pos = position(TOP);
mark_p->horiz_off = horiz_off;
mark_p->wraplines = wraplines;
if (mark_p->file) free(mark_p->file);
asprintf(&mark_p->file, "%s", current_file);
#define NTOP 4
/*
* Find the longest of the top NTOP lines and set that one to
* be the hint.
*
* A different algorithm would be to take the middle line shown
* and make that one to be the hint. Perhaps just taking the
* top line always would be better.
*
* There are many places in this program where introducing the
* concept of a ``highlight'' line would be useful.
*
* It's also very debatable which line is the correct line to take
* the line number from.
*/
for (maxlen = i = 0, npos = mark_p->pos; i < NTOP; i++) {
t = npos;
npos = forw_raw_line (npos);
if (strlen(line) >= maxlen) {
maxlen = strlen(line);
maxpos = t;
}
}
forw_raw_line(maxpos);
free (mark_p->hint);
mark_p->hint = strdup(line);
mark_p->linenum = find_linenum (mark_p->pos);
}
/*
* Put the hints and associated bookmark keys into a temporary file
* and run a copy of more(1) over the resulting hint file.
*
* If an error occurrs, returns -1 and sets the erreur and errstr.
*
* XXX We don't even come close to handling signals correctly here.
*/
int
marks_bookhints (void)
{
extern int top_scroll;
char *p;
int i, n;
int fname_width;
int fd;
FILE *out;
char cmd[MAXPATHLEN + 20];
char template[] = "/tmp/more.XXXXXXXXXX";
fd = mkstemp(template);
if (fd == -1) goto mkstemp_error;
out = fdopen (fd, "w");
if (out == NULL) goto fdopen_error;
/*
* Find the longest filename. We will find zero if the filenames are
* all the same.
*/
for (fname_width = i = 0; i < NMARKS; i++) {
if (marks[i].file && strlen(marks[i].file) >= fname_width)
fname_width = strlen(marks[i].file);
}
for (p = NULL, i = 0; i < NMARKS; i++) {
if (p && marks[i].file && strcmp(marks[i].file, p))
break;
else if (marks[i].file)
p = marks[i].file;
}
if (i == NMARKS)
fname_width = 0;
/*
* Print-out the header.
*/
errno = 0;
if (fname_width) {
n = fprintf (out, "key\t%.*s\tline#\tkeyline\n\n",
fname_width, "file");
} else {
n = fprintf (out, "key\tline#\tkeyline\n\n");
}
if (!n) goto fprintf_error;
/*
* Print-out the actual bookmarks.
*/
for (i = 0; i < NMARKS; i++)
{
int mchar;
if (i == LASTMARK)
mchar = '\'';
else
mchar = 'a' + i;
if (marks[i].file) {
if (fname_width) {
n = fprintf (out, "%c\t%.*s\t%d\t%s\n",
mchar, fname_width, marks[i].file,
marks[i].linenum, marks[i].hint);
} else {
n = fprintf (out, "%c\t%d\t%s\n",
mchar, marks[i].linenum, marks[i].hint);
}
}
if (!n) goto fprintf_error;
}
fclose (out);
/*
* Run a recursive copy of more on the hints file.
*/
snprintf (cmd, sizeof(cmd), "-more -ec %s", template);
lsystem(cmd);
if (unlink(template) == -1)
goto unlink_error;
return 0;
mkstemp_error:
SETERRSTR (E_SYSTEM, "mkstemp(): %s", strerror(errno));
return -1;
fdopen_error:
SETERRSTR (E_SYSTEM, "fdopen(): %s", strerror(errno));
return -1;
unlink_error:
SETERRSTR (E_SYSTEM, "unlink(): %s", strerror(errno));
return -1;
fprintf_error:
if (errno)
SETERRSTR (E_SYSTEM, strerror(errno));
else
SETERRSTR (E_SYSTEM, "fprintf() failed");
return -1;
}
/*
* Go to a previously set mark.
*/
gomark(c)
int c;
{
off_t pos;
char *file;
int new_horiz_off, new_wraplines;
extern char *current_file;
if (c == '\'') {
pos = marks[LASTMARK].pos;
if (pos == NULL_POSITION)
pos = 0;
file = marks[LASTMARK].file;
new_horiz_off = marks[LASTMARK].horiz_off;
new_wraplines = marks[LASTMARK].wraplines;
} else {
if (badmark(c))
return;
pos = marks[c-'a'].pos;
if (pos == NULL_POSITION) {
error("mark not set");
return;
}
file = marks[c-'a'].file;
new_horiz_off = marks[c-'a'].horiz_off;
new_wraplines = marks[c-'a'].wraplines;
}
/*
* This can only fail if gomark('\'') is called before lastmark()
* is called, which is in turn impossible since both edit() and
* jump_back() call jump_loc() which calls lastmark() to start
* all files.
*/
assert (file);
/*
* This can only fail if gomark() is called before any file has
* been opened. Calling gomark() before any file has been
* opened would be non-sensical.
*/
assert (current_file);
/*
* XXX The edit() needs to return success or failure so that we
* can abort at this point if edit() fails.
*/
edit(file, NO_FORCE_OPEN);
/* Try to be nice about changing the horizontal scroll and wrapping */
if (new_horiz_off > horiz_off + sc_width / 3 ||
new_horiz_off < horiz_off - sc_width / 3 ||
wraplines != new_wraplines || strcmp(file, current_file)) {
/*
* We should change horiz_off: if we don't change horiz_off
* the bookmarked location won't be readily visible.
*/
/*
* A prepaint() doesn't call lastmark() (jump_loc() does),
* but we need to call repaint() somewhere since we've
* changed the horizontal offset. We don't want to call
* jump_loc() followed by repaint() since that represents
* more unnecessary screen redrawing than I'm comfortable
* with. Manually calling lastmark() here means, however,
* that lastmark() is always called even if we scroll only
* a few lines --- unlike letting jump_loc() call lastmark()
* where lastmark() is only called for jumps of more than
* a screenful. A better interface is needed.
*/
lastmark();
horiz_off = new_horiz_off;
wraplines = new_wraplines;
prepaint(pos);
} else {
/*
* We can honour the bookmark request without doing any
* horizontal scrolling.
*/
jump_loc(pos);
}
}
syntax highlighted by Code2HTML, v. 0.9.1