MFV: less v668.

MFC after:	2 weeks
This commit is contained in:
Xin LI 2024-12-08 20:50:00 -08:00
commit c77c488926
65 changed files with 5751 additions and 2937 deletions

View file

@ -2,7 +2,7 @@
------------
Less
Copyright (C) 1984-2023 Mark Nudelman
Copyright (C) 1984-2024 Mark Nudelman
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions

View file

@ -9,6 +9,140 @@
Report bugs, suggestions or comments at
https://github.com/gwsw/less/issues.
======================================================================
Major changes between "less" versions 661 and 668
* Make 256/true colors work better on Windows without -Da
(github #539, github #546, github #562).
* Fix build using --with-secure (github #544).
* Fix crash when using --header on command line (github #545).
* Fix possible crash when scrolling left/right or toggling -S (github #547).
* Fix bug when using #stop in a lesskey file (github #551).
* Fix bug when using --shift or --match-shift on command line with
a parameter starting with '.' (github #554).
* Fix bug in R command when file size changes (github #553).
* Fix bug using --header when file does not fill screen (github #556).
* Fix ^X bug when output is not a terminal (github #558).
* Fix bug where ^Z is not handled immediately (github #563).
* Fix bug where first byte from a LESSOPEN filter is deleted if it is
greater than 0x7F (github #568).
* Fix uninitialized variable in edit_ifile (github #573).
* Fix incorrect handling of UTF-8 chars in prompts (github #576).
======================================================================
Major changes between "less" versions 643 and 661
* Add ^O^N, ^O^P, ^O^L and ^O^O commands and mouse clicks (with --mouse)
to find and open OSC8 hyperlinks (github #251).
* Add --match-shift option.
* Add --lesskey-content option (github #447).
* Add LESSKEY_CONTENT environment variable (github #447).
* Add --no-search-header-lines and --no-search-header-columns options
(github #397).
* Add ctrl-L search modifier (github #367).
* A ctrl-P at the start of a shell command suppresses the "done"
message (github #462).
* Add attribute characters ('*', '~', '_', '&') to --color
parameter (github #471).
* Allow expansion of environment variables in lesskey files.
* Add LESSSECURE_ALLOW environment variable (github #449).
* Add LESS_UNSUPPORT environment variable.
* Add line number parameter to --header option (github #436).
* Mouse right-click jumps to position marked by left-click (github #390).
* Ensure that the target line is not obscured by a header line
set by --header (github #444).
* Change default character set to "utf-8", except remains "dos" on MS-DOS.
* Add message when search with ^W wraps (github #459).
* UCRT builds on Windows 10 and later now support Unicode file names
(github #438).
* Improve behavior of interrupt while reading non-terminated pipe
(github #414).
* Improve parsing of -j, -x and -# options (github #393).
* Support files larger than 4GB on Windows (github #417).
* Support entry of Unicode chars larger than U+FFFF on Windows (github #391).
* Improve colors of bold, underline and standout text on Windows.
* Allow --rscroll to accept non-ASCII characters (github #483).
* Allow the parameter to certain options to be terminated with a
space (--color, --quotes, --rscroll, --search-options
and --intr) (github #495).
* Fix bug where # substitution failed after viewing help (github #420).
* Fix crash if files are deleted while less is viewing them (github #404).
* Workaround unreliable ReadConsoleInputW behavior on Windows
with non-ASCII input.
* Fix -J display when searching for non-ASCII characters (github #422).
* Don't filter header lines via the & command (github #423).
* Fix bug when horizontally shifting long lines (github #425).
* Add -x and -D options to lesstest, to make it easier to diagnose
a failed lesstest run.
* Fix bug searching long lines with --incsearch and -S (github #428).
* Fix bug that made ESC-} fail if top line on screen was empty (github #429).
* Fix bug with --mouse on Windows when used with pipes (github #440).
* Fix bug in --+OPTION command line syntax.
* Fix display bug when using -w with an empty line with a CR/LF
line ending (github #474).
* When substituting '#' or '%' with a filename, quote the filename
if it contains a space (github #480).
* Fix wrong sleep time when system has usleep but not nanosleep (github #489).
* Fix bug when file name contains a newline.
* Fix bug when file name contains nonprintable characters (github #503).
* Fix DJGPP build (github #497).
* Update Unicode tables.
======================================================================
Major changes between "less" versions 633 and 643

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -20,13 +20,6 @@
#include <windows.h>
#endif
#if HAVE_PROCFS
#include <sys/statfs.h>
#if HAVE_LINUX_MAGIC_H
#include <linux/magic.h>
#endif
#endif
typedef POSITION BLOCKNUM;
public int ignore_eoi;
@ -46,7 +39,7 @@ struct bufnode {
struct buf {
struct bufnode node;
BLOCKNUM block;
unsigned int datasize;
size_t datasize;
unsigned char data[LBUFSIZE];
};
#define bufnode_buf(bn) ((struct buf *) bn)
@ -64,7 +57,7 @@ struct filestate {
POSITION fpos;
int nbufs;
BLOCKNUM block;
unsigned int offset;
size_t offset;
POSITION fsize;
};
@ -122,15 +115,14 @@ struct filestate {
thisfile->hashtbl[h].hnext = (bn);
static struct filestate *thisfile;
static int ch_ungotchar = -1;
static unsigned char ch_ungotchar;
static lbool ch_have_ungotchar = FALSE;
static int maxbufs = -1;
extern int autobuf;
extern int sigs;
extern int secure;
extern int screen_trashed;
extern int follow_mode;
extern int waiting_for_data;
extern lbool waiting_for_data;
extern constant char helpdata[];
extern constant int size_helpdata;
extern IFILE curr_ifile;
@ -141,6 +133,13 @@ extern char *namelogfile;
static int ch_addbuf();
/*
* Return the file position corresponding to an offset within a block.
*/
static POSITION ch_position(BLOCKNUM block, size_t offset)
{
return (block * LBUFSIZE) + (POSITION) offset;
}
/*
* Get the character pointed to by the read pointer.
@ -149,8 +148,8 @@ static int ch_get(void)
{
struct buf *bp;
struct bufnode *bn;
int n;
int read_again;
ssize_t n;
lbool read_again;
int h;
POSITION pos;
POSITION len;
@ -187,6 +186,8 @@ static int ch_get(void)
goto found;
}
}
if (ABORT_SIGS())
return (EOI);
if (bn == END_OF_HCHAIN(h))
{
/*
@ -223,7 +224,7 @@ static int ch_get(void)
for (;;)
{
pos = (ch_block * LBUFSIZE) + bp->datasize;
pos = ch_position(ch_block, bp->datasize);
if ((len = ch_length()) != NULL_POSITION && pos >= len)
/*
* At end of file.
@ -239,7 +240,7 @@ static int ch_get(void)
*/
if (!(ch_flags & CH_CANSEEK))
return ('?');
if (lseek(ch_file, (off_t)pos, SEEK_SET) == BAD_LSEEK)
if (less_lseek(ch_file, (less_off_t)pos, SEEK_SET) == BAD_LSEEK)
{
error("seek error", NULL_PARG);
clear_eol();
@ -253,19 +254,18 @@ static int ch_get(void)
* If we read less than a full block, that's ok.
* We use partial block and pick up the rest next time.
*/
if (ch_ungotchar != -1)
if (ch_have_ungotchar)
{
bp->data[bp->datasize] = ch_ungotchar;
n = 1;
ch_ungotchar = -1;
ch_have_ungotchar = FALSE;
} else if (ch_flags & CH_HELPFILE)
{
bp->data[bp->datasize] = helpdata[ch_fpos];
bp->data[bp->datasize] = (unsigned char) helpdata[ch_fpos];
n = 1;
} else
{
n = iread(ch_file, &bp->data[bp->datasize],
(unsigned int)(LBUFSIZE - bp->datasize));
n = iread(ch_file, &bp->data[bp->datasize], LBUFSIZE - bp->datasize);
}
read_again = FALSE;
@ -295,12 +295,15 @@ static int ch_get(void)
/*
* If we have a log file, write the new data to it.
*/
if (!secure && logfile >= 0 && n > 0)
write(logfile, (char *) &bp->data[bp->datasize], n);
if (secure_allow(SF_LOGFILE))
{
if (logfile >= 0 && n > 0)
write(logfile, &bp->data[bp->datasize], (size_t) n);
}
#endif
ch_fpos += n;
bp->datasize += n;
bp->datasize += (size_t) n;
if (n == 0)
{
@ -323,7 +326,7 @@ static int ch_get(void)
if (ignore_eoi && follow_mode == FOLLOW_NAME && curr_ifile_changed())
{
/* screen_trashed=2 causes make_display to reopen the file. */
screen_trashed = 2;
screen_trashed_num(2);
return (EOI);
}
if (sigs)
@ -363,9 +366,15 @@ static int ch_get(void)
*/
public void ch_ungetchar(int c)
{
if (c != -1 && ch_ungotchar != -1)
error("ch_ungetchar overrun", NULL_PARG);
ch_ungotchar = c;
if (c < 0)
ch_have_ungotchar = FALSE;
else
{
if (ch_have_ungotchar)
error("ch_ungetchar overrun", NULL_PARG);
ch_ungotchar = (unsigned char) c;
ch_have_ungotchar = TRUE;
}
}
#if LOGFILE
@ -375,7 +384,7 @@ public void ch_ungetchar(int c)
*/
public void end_logfile(void)
{
static int tried = FALSE;
static lbool tried = FALSE;
if (logfile < 0)
return;
@ -402,7 +411,7 @@ public void sync_logfile(void)
{
struct buf *bp;
struct bufnode *bn;
int warned = FALSE;
lbool warned = FALSE;
BLOCKNUM block;
BLOCKNUM nblocks;
@ -411,13 +420,13 @@ public void sync_logfile(void)
nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE;
for (block = 0; block < nblocks; block++)
{
int wrote = FALSE;
lbool wrote = FALSE;
FOR_BUFS(bn)
{
bp = bufnode_buf(bn);
if (bp->block == block)
{
write(logfile, (char *) bp->data, bp->datasize);
write(logfile, bp->data, bp->datasize);
wrote = TRUE;
break;
}
@ -436,7 +445,7 @@ public void sync_logfile(void)
/*
* Determine if a specific block is currently in one of the buffers.
*/
static int buffered(BLOCKNUM block)
static lbool buffered(BLOCKNUM block)
{
struct buf *bp;
struct bufnode *bn;
@ -486,7 +495,7 @@ public int ch_seek(POSITION pos)
* Set read pointer.
*/
ch_block = new_block;
ch_offset = pos % LBUFSIZE;
ch_offset = (size_t) (pos % LBUFSIZE);
return (0);
}
@ -533,7 +542,7 @@ public int ch_end_buffer_seek(void)
FOR_BUFS(bn)
{
bp = bufnode_buf(bn);
buf_pos = (bp->block * LBUFSIZE) + bp->datasize;
buf_pos = ch_position(bp->block, bp->datasize);
if (buf_pos > end_pos)
end_pos = buf_pos;
}
@ -597,7 +606,7 @@ public POSITION ch_tell(void)
{
if (thisfile == NULL)
return (NULL_POSITION);
return (ch_block * LBUFSIZE) + ch_offset;
return ch_position(ch_block, ch_offset);
}
/*
@ -647,14 +656,14 @@ public int ch_back_get(void)
* Set max amount of buffer space.
* bufspace is in units of 1024 bytes. -1 mean no limit.
*/
public void ch_setbufspace(int bufspace)
public void ch_setbufspace(ssize_t bufspace)
{
if (bufspace < 0)
maxbufs = -1;
else
{
int lbufk = LBUFSIZE / 1024;
maxbufs = bufspace / lbufk + (bufspace % lbufk != 0);
size_t lbufk = LBUFSIZE / 1024;
maxbufs = (int) (bufspace / lbufk + (bufspace % lbufk != 0));
if (maxbufs < 1)
maxbufs = 1;
}
@ -688,11 +697,6 @@ public void ch_flush(void)
bufnode_buf(bn)->block = -1;
}
/*
* Figure out the size of the file, if we can.
*/
ch_fsize = filesize(ch_file);
/*
* Seek to a known position: the beginning of the file.
*/
@ -700,19 +704,16 @@ public void ch_flush(void)
ch_block = 0; /* ch_fpos / LBUFSIZE; */
ch_offset = 0; /* ch_fpos % LBUFSIZE; */
/*
* This is a kludge to workaround a Linux kernel bug: files in
* /proc have a size of 0 according to fstat() but have readable
* data. They are sometimes, but not always, seekable.
* Force them to be non-seekable here.
*/
if (ch_fsize == 0)
if (ch_flags & CH_NOTRUSTSIZE)
{
ch_fsize = NULL_POSITION;
ch_flags &= ~CH_CANSEEK;
} else
{
ch_fsize = (ch_flags & CH_HELPFILE) ? size_helpdata : filesize(ch_file);
}
if (lseek(ch_file, (off_t)0, SEEK_SET) == BAD_LSEEK)
if (less_lseek(ch_file, (less_off_t)0, SEEK_SET) == BAD_LSEEK)
{
/*
* Warning only; even if the seek fails for some reason,
@ -795,7 +796,7 @@ public int seekable(int f)
return (0);
}
#endif
return (lseek(f, (off_t)1, SEEK_SET) != BAD_LSEEK);
return (less_lseek(f, (less_off_t)1, SEEK_SET) != BAD_LSEEK);
}
/*
@ -812,7 +813,7 @@ public void ch_set_eof(void)
/*
* Initialize file state for a new file.
*/
public void ch_init(int f, int flags)
public void ch_init(int f, int flags, ssize_t nread)
{
/*
* See if we already have a filestate for this file.
@ -843,6 +844,22 @@ public void ch_init(int f, int flags)
}
if (thisfile->file == -1)
thisfile->file = f;
/*
* Figure out the size of the file, if we can.
*/
ch_fsize = (flags & CH_HELPFILE) ? size_helpdata : filesize(ch_file);
/*
* This is a kludge to workaround a Linux kernel bug: files in some
* pseudo filesystems like /proc and tracefs have a size of 0 according
* to fstat() but have readable data.
*/
if (ch_fsize == 0 && nread > 0)
{
ch_flags |= CH_NOTRUSTSIZE;
}
ch_flush();
}
@ -851,7 +868,7 @@ public void ch_init(int f, int flags)
*/
public void ch_close(void)
{
int keepstate = FALSE;
lbool keepstate = FALSE;
if (thisfile == NULL)
return;

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -120,8 +120,8 @@ struct cs_alias {
#define IS_CONTROL_CHAR 02
static char chardef[256];
static char *binfmt = NULL;
static char *utfbinfmt = NULL;
static constant char *binfmt = NULL;
static constant char *utfbinfmt = NULL;
public int binattr = AT_STANDOUT|AT_COLOR_BIN;
static struct xbuffer user_wide_array;
@ -139,13 +139,13 @@ static struct wchar_range_table user_prt_table;
static void wchar_range_table_set(struct wchar_range_table *tbl, struct xbuffer *arr)
{
tbl->table = (struct wchar_range *) arr->data;
tbl->count = arr->end / sizeof(struct wchar_range);
tbl->count = (unsigned int) (arr->end / sizeof(struct wchar_range));
}
/*
* Skip over a "U" or "U+" prefix before a hex codepoint.
*/
static char * skip_uprefix(char *s)
static constant char * skip_uprefix(constant char *s)
{
if (*s == 'U' || *s == 'u')
if (*++s == '+') ++s;
@ -155,14 +155,14 @@ static char * skip_uprefix(char *s)
/*
* Parse a dash-separated range of hex values.
*/
static void wchar_range_get(char **ss, struct wchar_range *range)
static void wchar_range_get(constant char **ss, struct wchar_range *range)
{
char *s = skip_uprefix(*ss);
range->first = lstrtoul(s, &s, 16);
constant char *s = skip_uprefix(*ss);
range->first = lstrtoulc(s, &s, 16);
if (s[0] == '-')
{
s = skip_uprefix(&s[1]);
range->last = lstrtoul(s, &s, 16);
range->last = lstrtoulc(s, &s, 16);
} else
{
range->last = range->first;
@ -173,7 +173,7 @@ static void wchar_range_get(char **ss, struct wchar_range *range)
/*
* Parse the LESSUTFCHARDEF variable.
*/
static void ichardef_utf(char *s)
static void ichardef_utf(constant char *s)
{
xbuf_init(&user_wide_array);
xbuf_init(&user_ubin_array);
@ -241,7 +241,7 @@ static void ichardef_utf(char *s)
* b binary character
* c control character
*/
static void ichardef(char *s)
static void ichardef(constant char *s)
{
char *cp;
int n;
@ -298,7 +298,7 @@ static void ichardef(char *s)
* Define a charset, given a charset name.
* The valid charset names are listed in the "charsets" array.
*/
static int icharset(char *name, int no_error)
static int icharset(constant char *name, int no_error)
{
struct charset *p;
struct cs_alias *a;
@ -363,23 +363,8 @@ static void ilocale(void)
/*
* Define the printing format for control (or binary utf) chars.
*/
public void setfmt(char *s, char **fmtvarptr, int *attrptr, char *default_fmt, int for_printf)
public void setfmt(constant char *s, constant char **fmtvarptr, int *attrptr, constant char *default_fmt, lbool for_printf)
{
if (s && utf_mode)
{
/* It would be too hard to account for width otherwise. */
char constant *t = s;
while (*t)
{
if (*t < ' ' || *t > '~')
{
s = default_fmt;
goto attr;
}
t++;
}
}
if (s == NULL || *s == '\0')
s = default_fmt;
else if (for_printf &&
@ -391,7 +376,6 @@ public void setfmt(char *s, char **fmtvarptr, int *attrptr, char *default_fmt, i
/*
* Select the attributes if it starts with "*".
*/
attr:
if (*s == '*' && s[1] != '\0')
{
switch (s[1])
@ -412,16 +396,7 @@ public void setfmt(char *s, char **fmtvarptr, int *attrptr, char *default_fmt, i
*/
static void set_charset(void)
{
char *s;
#if MSDOS_COMPILER==WIN32C
/*
* If the Windows console is using UTF-8, we'll use it too.
*/
if (GetConsoleOutputCP() == CP_UTF8)
if (icharset("utf-8", 1))
return;
#endif
constant char *s;
ichardef_utf(lgetenv("LESSUTFCHARDEF"));
@ -476,15 +451,13 @@ static void set_charset(void)
ilocale();
#else
#if MSDOS_COMPILER
/*
* Default to "dos".
*/
(void) icharset("dos", 1);
#if MSDOS_COMPILER==WIN32C
(void) icharset("utf-8", 1);
#else
/*
* Default to "latin1".
*/
(void) icharset("latin1", 1);
(void) icharset("dos", 1);
#endif
#else
(void) icharset("utf-8", 1);
#endif
#endif
}
@ -494,7 +467,7 @@ static void set_charset(void)
*/
public void init_charset(void)
{
char *s;
constant char *s;
#if HAVE_LOCALE
setlocale(LC_ALL, "");
@ -512,20 +485,22 @@ public void init_charset(void)
/*
* Is a given character a "binary" character?
*/
public int binary_char(LWCHAR c)
public lbool binary_char(LWCHAR c)
{
if (utf_mode)
return (is_ubin_char(c));
c &= 0377;
return (chardef[c] & IS_BINARY_CHAR);
if (c >= sizeof(chardef))
return TRUE;
return ((chardef[c] & IS_BINARY_CHAR) != 0);
}
/*
* Is a given character a "control" character?
*/
public int control_char(LWCHAR c)
public lbool control_char(LWCHAR c)
{
c &= 0377;
if (c >= sizeof(chardef))
return TRUE;
return (chardef[c] & IS_CONTROL_CHAR);
}
@ -533,12 +508,12 @@ public int control_char(LWCHAR c)
* Return the printable form of a character.
* For example, in the "ascii" charset '\3' is printed as "^C".
*/
public char * prchar(LWCHAR c)
public constant char * prchar(LWCHAR c)
{
/* {{ This buffer can be overrun if LESSBINFMT is a long string. }} */
/* {{ Fixed buffer size means LESSBINFMT etc can be truncated. }} */
static char buf[MAX_PRCHAR_LEN+1];
c &= 0377;
c &= 0377; /*{{type-issue}}*/
if ((c < 128 || !utf_mode) && !control_char(c))
SNPRINTF1(buf, sizeof(buf), "%c", (int) c);
else if (c == ESC)
@ -567,7 +542,7 @@ public char * prchar(LWCHAR c)
/*
* Return the printable form of a UTF-8 character.
*/
public char * prutfchar(LWCHAR ch)
public constant char * prutfchar(LWCHAR ch)
{
static char buf[MAX_PRCHAR_LEN+1];
@ -596,7 +571,7 @@ public char * prutfchar(LWCHAR ch)
/*
* Get the length of a UTF-8 character in bytes.
*/
public int utf_len(int ch)
public int utf_len(char ch)
{
if ((ch & 0x80) == 0)
return 1;
@ -606,10 +581,12 @@ public int utf_len(int ch)
return 3;
if ((ch & 0xF8) == 0xF0)
return 4;
#if 0
if ((ch & 0xFC) == 0xF8)
return 5;
if ((ch & 0xFE) == 0xFC)
return 6;
#endif
/* Invalid UTF-8 encoding. */
return 1;
}
@ -617,42 +594,41 @@ public int utf_len(int ch)
/*
* Does the parameter point to the lead byte of a well-formed UTF-8 character?
*/
public int is_utf8_well_formed(char *ss, int slen)
public lbool is_utf8_well_formed(constant char *ss, int slen)
{
int i;
int len;
unsigned char *s = (unsigned char *) ss;
unsigned char s0 = (unsigned char) ss[0];
if (IS_UTF8_INVALID(s[0]))
return (0);
if (IS_UTF8_INVALID(s0))
return (FALSE);
len = utf_len(s[0]);
len = utf_len(ss[0]);
if (len > slen)
return (0);
return (FALSE);
if (len == 1)
return (1);
return (TRUE);
if (len == 2)
{
if (s[0] < 0xC2)
return (0);
if (s0 < 0xC2)
return (FALSE);
} else
{
unsigned char mask;
mask = (~((1 << (8-len)) - 1)) & 0xFF;
if (s[0] == mask && (s[1] & mask) == 0x80)
return (0);
unsigned char mask = (unsigned char) (~((1 << (8-len)) - 1));
if (s0 == mask && (ss[1] & mask) == 0x80)
return (FALSE);
}
for (i = 1; i < len; i++)
if (!IS_UTF8_TRAIL(s[i]))
return (0);
return (1);
if (!IS_UTF8_TRAIL(ss[i]))
return (FALSE);
return (TRUE);
}
/*
* Skip bytes until a UTF-8 lead byte (11xxxxxx) or ASCII byte (0xxxxxxx) is found.
*/
public void utf_skip_to_lead(char **pp, char *limit)
public void utf_skip_to_lead(constant char **pp, constant char *limit)
{
do {
++(*pp);
@ -663,9 +639,10 @@ public void utf_skip_to_lead(char **pp, char *limit)
/*
* Get the value of a UTF-8 character.
*/
public LWCHAR get_wchar(constant char *p)
public LWCHAR get_wchar(constant char *sp)
{
switch (utf_len(p[0]))
constant unsigned char *p = (constant unsigned char *) sp;
switch (utf_len(sp[0]))
{
case 1:
default:
@ -690,6 +667,7 @@ public LWCHAR get_wchar(constant char *p)
((p[1] & 0x3F) << 12) |
((p[2] & 0x3F) << 6) |
(p[3] & 0x3F));
#if 0
case 5:
/* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
return (LWCHAR) (
@ -707,13 +685,14 @@ public LWCHAR get_wchar(constant char *p)
((p[3] & 0x3F) << 12) |
((p[4] & 0x3F) << 6) |
(p[5] & 0x3F));
#endif
}
}
/*
* Store a character into a UTF-8 string.
*/
public void put_wchar(char **pp, LWCHAR ch)
public void put_wchar(mutable char **pp, LWCHAR ch)
{
if (!utf_mode || ch < 0x80)
{
@ -737,6 +716,7 @@ public void put_wchar(char **pp, LWCHAR ch)
*(*pp)++ = (char) (0x80 | ((ch >> 12) & 0x3F));
*(*pp)++ = (char) (0x80 | ((ch >> 6) & 0x3F));
*(*pp)++ = (char) (0x80 | (ch & 0x3F));
#if 0
} else if (ch < 0x4000000)
{
/* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
@ -754,17 +734,18 @@ public void put_wchar(char **pp, LWCHAR ch)
*(*pp)++ = (char) (0x80 | ((ch >> 12) & 0x3F));
*(*pp)++ = (char) (0x80 | ((ch >> 6) & 0x3F));
*(*pp)++ = (char) (0x80 | (ch & 0x3F));
#endif
}
}
/*
* Step forward or backward one character in a string.
*/
public LWCHAR step_char(char **pp, signed int dir, constant char *limit)
public LWCHAR step_charc(constant char **pp, signed int dir, constant char *limit)
{
LWCHAR ch;
int len;
char *p = *pp;
constant char *p = *pp;
if (!utf_mode)
{
@ -798,6 +779,14 @@ public LWCHAR step_char(char **pp, signed int dir, constant char *limit)
return ch;
}
public LWCHAR step_char(char **pp, signed int dir, constant char *limit)
{
constant char *p = (constant char *) *pp;
LWCHAR ch = step_charc(&p, dir, limit);
*pp = (char *) p;
return ch;
}
/*
* Unicode characters data
* Actual data is in the generated *.uni files.
@ -806,7 +795,7 @@ public LWCHAR step_char(char **pp, signed int dir, constant char *limit)
#define DECLARE_RANGE_TABLE_START(name) \
static struct wchar_range name##_array[] = {
#define DECLARE_RANGE_TABLE_END(name) \
}; struct wchar_range_table name##_table = { name##_array, sizeof(name##_array)/sizeof(*name##_array) };
}; struct wchar_range_table name##_table = { name##_array, countof(name##_array) };
DECLARE_RANGE_TABLE_START(compose)
#include "compose.uni"
@ -830,36 +819,36 @@ static struct wchar_range comb_table[] = {
};
static int is_in_table(LWCHAR ch, struct wchar_range_table *table)
static lbool is_in_table(LWCHAR ch, struct wchar_range_table *table)
{
int hi;
int lo;
unsigned int hi;
unsigned int lo;
/* Binary search in the table. */
if (table->table == NULL || table->count == 0 || ch < table->table[0].first)
return 0;
return FALSE;
lo = 0;
hi = table->count - 1;
while (lo <= hi)
{
int mid = (lo + hi) / 2;
unsigned int mid = (lo + hi) / 2;
if (ch > table->table[mid].last)
lo = mid + 1;
else if (ch < table->table[mid].first)
hi = mid - 1;
else
return 1;
return TRUE;
}
return 0;
return FALSE;
}
/*
* Is a character a UTF-8 composing character?
* If a composing character follows any char, the two combine into one glyph.
*/
public int is_composing_char(LWCHAR ch)
public lbool is_composing_char(LWCHAR ch)
{
if (is_in_table(ch, &user_prt_table)) return 0;
if (is_in_table(ch, &user_prt_table)) return FALSE;
return is_in_table(ch, &user_compose_table) ||
is_in_table(ch, &compose_table) ||
(bs_mode != BS_CONTROL && is_in_table(ch, &fmt_table));
@ -868,9 +857,9 @@ public int is_composing_char(LWCHAR ch)
/*
* Should this UTF-8 character be treated as binary?
*/
public int is_ubin_char(LWCHAR ch)
public lbool is_ubin_char(LWCHAR ch)
{
if (is_in_table(ch, &user_prt_table)) return 0;
if (is_in_table(ch, &user_prt_table)) return FALSE;
return is_in_table(ch, &user_ubin_table) ||
is_in_table(ch, &ubin_table) ||
(bs_mode == BS_CONTROL && is_in_table(ch, &fmt_table));
@ -879,7 +868,7 @@ public int is_ubin_char(LWCHAR ch)
/*
* Is this a double width UTF-8 character?
*/
public int is_wide_char(LWCHAR ch)
public lbool is_wide_char(LWCHAR ch)
{
return is_in_table(ch, &user_wide_table) ||
is_in_table(ch, &wide_table);
@ -890,16 +879,16 @@ public int is_wide_char(LWCHAR ch)
* A combining char acts like an ordinary char, but if it follows
* a specific char (not any char), the two combine into one glyph.
*/
public int is_combining_char(LWCHAR ch1, LWCHAR ch2)
public lbool is_combining_char(LWCHAR ch1, LWCHAR ch2)
{
/* The table is small; use linear search. */
int i;
for (i = 0; i < sizeof(comb_table)/sizeof(*comb_table); i++)
for (i = 0; i < countof(comb_table); i++)
{
if (ch1 == comb_table[i].first &&
ch2 == comb_table[i].last)
return 1;
return TRUE;
}
return 0;
return FALSE;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -77,6 +77,10 @@
#define A_X116MOUSE_IN 68
#define A_PSHELL 69
#define A_CLR_SEARCH 70
#define A_OSC8_F_SEARCH 71
#define A_OSC8_B_SEARCH 72
#define A_OSC8_OPEN 73
#define A_OSC8_JUMP 74
/* These values must not conflict with any A_* or EC_* value. */
#define A_INVALID 100

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -24,26 +24,26 @@ extern int sc_width;
extern int utf_mode;
extern int no_hist_dups;
extern int marks_modified;
extern int secure;
static char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */
static int cmd_col; /* Current column of the cursor */
static int prompt_col; /* Column of cursor just after prompt */
static char *cp; /* Pointer into cmdbuf */
static int cmd_offset; /* Index into cmdbuf of first displayed char */
static int literal; /* Next input char should not be interpreted */
public int updown_match = -1; /* Prefix length in up/down movement */
static lbool literal; /* Next input char should not be interpreted */
static size_t updown_match; /* Prefix length in up/down movement */
static lbool have_updown_match = FALSE;
#if TAB_COMPLETE_FILENAME
static int cmd_complete(int action);
/*
* These variables are statics used by cmd_complete.
*/
static int in_completion = 0;
static lbool in_completion = FALSE;
static char *tk_text;
static char *tk_original;
static char *tk_ipoint;
static char *tk_trial = NULL;
static constant char *tk_ipoint;
static constant char *tk_trial = NULL;
static struct textlist tk_tlist;
#endif
@ -72,7 +72,7 @@ struct mlist
struct mlist *prev;
struct mlist *curr_mp;
char *string;
int modified;
lbool modified;
};
/*
@ -123,9 +123,9 @@ public void cmd_reset(void)
*cp = '\0';
cmd_col = 0;
cmd_offset = 0;
literal = 0;
literal = FALSE;
cmd_mbc_buf_len = 0;
updown_match = -1;
have_updown_match = FALSE;
}
/*
@ -135,7 +135,7 @@ public void clear_cmd(void)
{
cmd_col = prompt_col = 0;
cmd_mbc_buf_len = 0;
updown_match = -1;
have_updown_match = FALSE;
}
/*
@ -148,11 +148,11 @@ public void cmd_putstr(constant char *s)
constant char *endline = s + strlen(s);
while (*s != '\0')
{
char *ns = (char *) s;
constant char *os = s;
int width;
ch = step_char(&ns, +1, endline);
while (s < ns)
putchr(*s++);
ch = step_charc(&s, +1, endline);
while (os < s)
putchr(*os++);
if (!utf_mode)
width = 1;
else if (is_composing_char(ch) || is_combining_char(prev_ch, ch))
@ -170,13 +170,13 @@ public void cmd_putstr(constant char *s)
*/
public int len_cmdbuf(void)
{
char *s = cmdbuf;
char *endline = s + strlen(s);
constant char *s = cmdbuf;
constant char *endline = s + strlen(s);
int len = 0;
while (*s != '\0')
{
step_char(&s, +1, endline);
step_charc(&s, +1, endline);
len++;
}
return (len);
@ -187,14 +187,14 @@ public int len_cmdbuf(void)
* {{ Returning pwidth and bswidth separately is a historical artifact
* since they're always the same. Maybe clean this up someday. }}
*/
static char * cmd_step_common(char *p, LWCHAR ch, int len, int *pwidth, int *bswidth)
static constant char * cmd_step_common(char *p, LWCHAR ch, size_t len, int *pwidth, int *bswidth)
{
char *pr;
constant char *pr;
int width;
if (len == 1)
{
pr = prchar((int) ch);
pr = prchar(ch);
width = (int) strlen(pr);
} else
{
@ -222,23 +222,23 @@ static char * cmd_step_common(char *p, LWCHAR ch, int len, int *pwidth, int *bsw
/*
* Step a pointer one character right in the command buffer.
*/
static char * cmd_step_right(char **pp, int *pwidth, int *bswidth)
static constant char * cmd_step_right(char **pp, int *pwidth, int *bswidth)
{
char *p = *pp;
LWCHAR ch = step_char(pp, +1, p + strlen(p));
return cmd_step_common(p, ch, *pp - p, pwidth, bswidth);
return cmd_step_common(p, ch, ptr_diff(*pp, p), pwidth, bswidth);
}
/*
* Step a pointer one character left in the command buffer.
*/
static char * cmd_step_left(char **pp, int *pwidth, int *bswidth)
static constant char * cmd_step_left(char **pp, int *pwidth, int *bswidth)
{
char *p = *pp;
LWCHAR ch = step_char(pp, -1, cmdbuf);
return cmd_step_common(*pp, ch, p - *pp, pwidth, bswidth);
return cmd_step_common(*pp, ch, ptr_diff(p, *pp), pwidth, bswidth);
}
/*
@ -279,7 +279,7 @@ public void cmd_repaint(constant char *old_cp)
{
char *np = cp;
int width;
char *pr = cmd_step_right(&np, &width, NULL);
constant char *pr = cmd_step_right(&np, &width, NULL);
if (cmd_col + width >= sc_width)
break;
cp = np;
@ -290,7 +290,7 @@ public void cmd_repaint(constant char *old_cp)
{
char *np = cp;
int width;
char *pr = cmd_step_right(&np, &width, NULL);
constant char *pr = cmd_step_right(&np, &width, NULL);
if (width > 0)
break;
cp = np;
@ -375,7 +375,7 @@ static void cmd_rshift(void)
*/
static int cmd_right(void)
{
char *pr;
constant char *pr;
char *ncp;
int width;
@ -437,7 +437,7 @@ static int cmd_left(void)
/*
* Insert a char into the command buffer, at the current position.
*/
static int cmd_ichar(char *cs, int clen)
static int cmd_ichar(constant char *cs, size_t clen)
{
char *s;
@ -461,7 +461,7 @@ static int cmd_ichar(char *cs, int clen)
/*
* Reprint the tail of the line from the inserted char.
*/
updown_match = -1;
have_updown_match = FALSE;
cmd_repaint(cp);
cmd_right();
return (CC_OK);
@ -504,7 +504,7 @@ static int cmd_erase(void)
/*
* Repaint the buffer after the erased char.
*/
updown_match = -1;
have_updown_match = FALSE;
cmd_repaint(cp);
/*
@ -597,7 +597,7 @@ static int cmd_kill(void)
cmd_offset = 0;
cmd_home();
*cp = '\0';
updown_match = -1;
have_updown_match = FALSE;
cmd_repaint(cp);
/*
@ -644,9 +644,10 @@ static int cmd_updown(int action)
return (CC_OK);
}
if (updown_match < 0)
if (!have_updown_match)
{
updown_match = (int) (cp - cmdbuf);
updown_match = ptr_diff(cp, cmdbuf);
have_updown_match = TRUE;
}
/*
@ -687,7 +688,23 @@ static int cmd_updown(int action)
bell();
return (CC_OK);
}
#endif
/*
* Yet another lesson in the evils of global variables.
*/
public ssize_t save_updown_match(void)
{
if (!have_updown_match)
return (ssize_t)(-1);
return (ssize_t) updown_match;
}
public void restore_updown_match(ssize_t udm)
{
updown_match = udm;
have_updown_match = (udm != (ssize_t)(-1));
}
#endif /* CMD_HISTORY */
/*
*
@ -712,7 +729,7 @@ static void ml_unlink(struct mlist *ml)
/*
* Add a string to an mlist.
*/
public void cmd_addhist(struct mlist *mlist, constant char *cmd, int modified)
public void cmd_addhist(struct mlist *mlist, constant char *cmd, lbool modified)
{
#if CMD_HISTORY
struct mlist *ml;
@ -774,8 +791,8 @@ public void cmd_accept(void)
*/
if (curr_mlist == NULL || curr_mlist == ml_examine)
return;
cmd_addhist(curr_mlist, cmdbuf, 1);
curr_mlist->modified = 1;
cmd_addhist(curr_mlist, cmdbuf, TRUE);
curr_mlist->modified = TRUE;
#endif
}
@ -787,7 +804,7 @@ public void cmd_accept(void)
* CC_OK Line edit function done.
* CC_QUIT The char requests the current command to be aborted.
*/
static int cmd_edit(int c)
static int cmd_edit(char c)
{
int action;
int flags;
@ -878,7 +895,7 @@ static int cmd_edit(int c)
not_in_completion();
return (cmd_wdelete());
case EC_LITERAL:
literal = 1;
literal = TRUE;
return (CC_OK);
#if CMD_HISTORY
case EC_UP:
@ -902,17 +919,17 @@ static int cmd_edit(int c)
/*
* Insert a string into the command buffer, at the current position.
*/
static int cmd_istr(char *str)
static int cmd_istr(constant char *str)
{
char *s;
constant char *endline = str + strlen(str);
constant char *s;
int action;
char *endline = str + strlen(str);
for (s = str; *s != '\0'; )
{
char *os = s;
step_char(&s, +1, endline);
action = cmd_ichar(os, s - os);
constant char *os = s;
step_charc(&s, +1, endline);
action = cmd_ichar(os, ptr_diff(s, os));
if (action != CC_OK)
return (action);
}
@ -930,10 +947,10 @@ static char * delimit_word(void)
char *word;
#if SPACES_IN_FILENAMES
char *p;
int delim_quoted = 0;
int meta_quoted = 0;
int delim_quoted = FALSE;
int meta_quoted = FALSE;
constant char *esc = get_meta_escape();
int esclen = (int) strlen(esc);
size_t esclen = strlen(esc);
#endif
/*
@ -984,20 +1001,20 @@ static char * delimit_word(void)
{
if (meta_quoted)
{
meta_quoted = 0;
meta_quoted = FALSE;
} else if (esclen > 0 && p + esclen < cp &&
strncmp(p, esc, esclen) == 0)
{
meta_quoted = 1;
meta_quoted = TRUE;
p += esclen - 1;
} else if (delim_quoted)
{
if (*p == closequote)
delim_quoted = 0;
delim_quoted = FALSE;
} else /* (!delim_quoted) */
{
if (*p == openquote)
delim_quoted = 1;
delim_quoted = TRUE;
else if (*p == ' ')
word = p+1;
}
@ -1040,8 +1057,8 @@ static void init_compl(void)
*/
if (tk_original != NULL)
free(tk_original);
tk_original = (char *) ecalloc(cp-word+1, sizeof(char));
strncpy(tk_original, word, cp-word);
tk_original = (char *) ecalloc(ptr_diff(cp,word)+1, sizeof(char));
strncpy(tk_original, word, ptr_diff(cp,word));
/*
* Get the expanded filename.
* This may result in a single filename, or
@ -1073,7 +1090,7 @@ static void init_compl(void)
/*
* Return the next word in the current completion list.
*/
static char * next_compl(int action, char *prev)
static constant char * next_compl(int action, constant char *prev)
{
switch (action)
{
@ -1094,7 +1111,7 @@ static char * next_compl(int action, char *prev)
*/
static int cmd_complete(int action)
{
char *s;
constant char *s;
if (!in_completion || action == EC_EXPAND)
{
@ -1120,7 +1137,7 @@ static int cmd_complete(int action)
/*
* Use the first filename in the list.
*/
in_completion = 1;
in_completion = TRUE;
init_textlist(&tk_tlist, tk_text);
tk_trial = next_compl(action, (char*)NULL);
}
@ -1145,7 +1162,7 @@ static int cmd_complete(int action)
* There are no more trial completions.
* Insert the original (uncompleted) filename.
*/
in_completion = 0;
in_completion = FALSE;
if (cmd_istr(tk_original) != CC_OK)
goto fail;
} else
@ -1173,7 +1190,7 @@ static int cmd_complete(int action)
return (CC_OK);
fail:
in_completion = 0;
in_completion = FALSE;
bell();
return (CC_OK);
}
@ -1188,10 +1205,10 @@ fail:
* CC_QUIT The char requests the command to be aborted.
* CC_ERROR The char could not be accepted due to an error.
*/
public int cmd_char(int c)
public int cmd_char(char c)
{
int action;
int len;
size_t len;
if (!utf_mode)
{
@ -1208,7 +1225,7 @@ public int cmd_char(int c)
if (IS_ASCII_OCTET(c))
cmd_mbc_buf_len = 1;
#if MSDOS_COMPILER || OS2
else if (c == (unsigned char) '\340' && IS_ASCII_OCTET(peekcc()))
else if (c == '\340' && IS_ASCII_OCTET(peekcc()))
{
/* Assume a special key. */
cmd_mbc_buf_len = 1;
@ -1245,7 +1262,7 @@ public int cmd_char(int c)
goto retry;
}
len = cmd_mbc_buf_len;
len = (size_t) cmd_mbc_buf_len; /*{{type-issue}}*/
cmd_mbc_buf_len = 0;
}
@ -1254,7 +1271,7 @@ public int cmd_char(int c)
/*
* Insert the char, even if it is a line-editing char.
*/
literal = 0;
literal = FALSE;
return (cmd_ichar(cmd_mbc_buf, len));
}
@ -1283,11 +1300,11 @@ public int cmd_char(int c)
/*
* Return the number currently in the command buffer.
*/
public LINENUM cmd_int(long *frac)
public LINENUM cmd_int(mutable long *frac)
{
char *p;
constant char *p;
LINENUM n = 0;
int err;
lbool err;
for (p = cmdbuf; *p >= '0' && *p <= '9'; p++)
{
@ -1309,7 +1326,7 @@ public LINENUM cmd_int(long *frac)
/*
* Return a pointer to the command buffer.
*/
public char * get_cmdbuf(void)
public constant char * get_cmdbuf(void)
{
if (cmd_mbc_buf_index < cmd_mbc_buf_len)
/* Don't return buffer containing an incomplete multibyte char. */
@ -1321,7 +1338,7 @@ public char * get_cmdbuf(void)
/*
* Return the last (most recent) string in the current command history.
*/
public char * cmd_lastpattern(void)
public constant char * cmd_lastpattern(void)
{
if (curr_mlist == NULL)
return (NULL);
@ -1343,9 +1360,9 @@ static int mlist_size(struct mlist *ml)
/*
* Get the name of the history file.
*/
static char * histfile_find(int must_exist)
static char * histfile_find(lbool must_exist)
{
char *home = lgetenv("HOME");
constant char *home = lgetenv("HOME");
char *name = NULL;
/* Try in $XDG_STATE_HOME, then in $HOME/.local/state, then in $XDG_DATA_HOME, then in $HOME. */
@ -1370,9 +1387,10 @@ static char * histfile_find(int must_exist)
return (name);
}
static char * histfile_name(int must_exist)
static char * histfile_name(lbool must_exist)
{
char *name;
constant char *name;
char *wname;
/* See if filename is explicitly specified by $LESSHISTFILE. */
name = lgetenv("LESSHISTFILE");
@ -1388,30 +1406,29 @@ static char * histfile_name(int must_exist)
if (strcmp(LESSHISTFILE, "") == 0 || strcmp(LESSHISTFILE, "-") == 0)
return (NULL);
name = NULL;
wname = NULL;
if (!must_exist)
{
/* If we're writing the file and the file already exists, use it. */
name = histfile_find(1);
wname = histfile_find(TRUE);
}
if (name == NULL)
name = histfile_find(must_exist);
return (name);
if (wname == NULL)
wname = histfile_find(must_exist);
return (wname);
}
/*
* Read a .lesshst file and call a callback for each line in the file.
*/
static void read_cmdhist2(void (*action)(void*,struct mlist*,char*), void *uparam, int skip_search, int skip_shell)
static void read_cmdhist2(void (*action)(void*,struct mlist*,constant char*), void *uparam, int skip_search, int skip_shell)
{
struct mlist *ml = NULL;
char line[CMDBUF_SIZE];
char *filename;
FILE *f;
char *p;
int *skip = NULL;
filename = histfile_name(1);
filename = histfile_name(TRUE);
if (filename == NULL)
return;
f = fopen(filename, "r");
@ -1426,6 +1443,7 @@ static void read_cmdhist2(void (*action)(void*,struct mlist*,char*), void *upara
}
while (fgets(line, sizeof(line), f) != NULL)
{
char *p;
for (p = line; *p != '\0'; p++)
{
if (*p == '\n' || *p == '\r')
@ -1467,20 +1485,21 @@ static void read_cmdhist2(void (*action)(void*,struct mlist*,char*), void *upara
fclose(f);
}
static void read_cmdhist(void (*action)(void*,struct mlist*,char*), void *uparam, int skip_search, int skip_shell)
static void read_cmdhist(void (*action)(void*,struct mlist*,constant char*), void *uparam, lbool skip_search, lbool skip_shell)
{
if (secure)
if (!secure_allow(SF_HISTORY))
return;
read_cmdhist2(action, uparam, skip_search, skip_shell);
(*action)(uparam, NULL, NULL); /* signal end of file */
}
static void addhist_init(void *uparam, struct mlist *ml, char *string)
static void addhist_init(void *uparam, struct mlist *ml, constant char *string)
{
(void) uparam;
if (ml != NULL)
cmd_addhist(ml, string, 0);
else if (string != NULL)
restore_mark((char*)string); /* stupid const cast */
restore_mark(string);
}
#endif /* CMD_HISTORY */
@ -1518,15 +1537,15 @@ static void write_mlist(struct mlist *ml, FILE *f)
if (!ml->modified)
continue;
fprintf(f, "\"%s\n", ml->string);
ml->modified = 0;
ml->modified = FALSE;
}
ml->modified = 0; /* entire mlist is now unmodified */
ml->modified = FALSE; /* entire mlist is now unmodified */
}
/*
* Make a temp name in the same directory as filename.
*/
static char * make_tempname(char *filename)
static char * make_tempname(constant char *filename)
{
char lastch;
char *tempname = ecalloc(1, strlen(filename)+1);
@ -1547,7 +1566,7 @@ struct save_ctx
* At the end of each mlist, append any new entries
* created during this session.
*/
static void copy_hist(void *uparam, struct mlist *ml, char *string)
static void copy_hist(void *uparam, struct mlist *ml, constant char *string)
{
struct save_ctx *ctx = (struct save_ctx *) uparam;
@ -1591,13 +1610,13 @@ static void copy_hist(void *uparam, struct mlist *ml, char *string)
static void make_file_private(FILE *f)
{
#if HAVE_FCHMOD
int do_chmod = 1;
lbool do_chmod = TRUE;
#if HAVE_STAT
struct stat statbuf;
int r = fstat(fileno(f), &statbuf);
if (r < 0 || !S_ISREG(statbuf.st_mode))
/* Don't chmod if not a regular file. */
do_chmod = 0;
do_chmod = FALSE;
#endif
if (do_chmod)
fchmod(fileno(f), 0600);
@ -1608,17 +1627,17 @@ static void make_file_private(FILE *f)
* Does the history file need to be updated?
*/
#if CMD_HISTORY
static int histfile_modified(void)
static lbool histfile_modified(void)
{
if (mlist_search.modified)
return 1;
return TRUE;
#if SHELL_ESCAPE || PIPEC
if (mlist_shell.modified)
return 1;
return TRUE;
#endif
if (marks_modified)
return 1;
return 0;
return TRUE;
return FALSE;
}
#endif
@ -1633,11 +1652,11 @@ public void save_cmdhist(void)
int skip_search;
int skip_shell;
struct save_ctx ctx;
char *s;
constant char *s;
FILE *fout = NULL;
int histsize = 0;
if (secure || !histfile_modified())
if (!secure_allow(SF_HISTORY) || !histfile_modified())
return;
histname = histfile_name(0);
if (histname == NULL)

View file

@ -1,6 +1,6 @@
/* $FreeBSD$ */
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -25,7 +25,6 @@ extern int erase_char, erase2_char, kill_char;
extern int sigs;
extern int quit_if_one_screen;
extern int one_screen;
extern int squished;
extern int sc_width;
extern int sc_height;
extern char *kent;
@ -35,16 +34,12 @@ extern int quitting;
extern int wscroll;
extern int top_scroll;
extern int ignore_eoi;
extern int secure;
extern int hshift;
extern int bs_mode;
extern int proc_backspace;
extern int show_attn;
extern int less_is_more;
extern int status_col;
extern POSITION highest_hilite;
extern POSITION start_attnpos;
extern POSITION end_attnpos;
extern char *every_first_cmd;
extern char version[];
extern struct scrpos initial_scrpos;
@ -52,24 +47,24 @@ extern IFILE curr_ifile;
extern void *ml_search;
extern void *ml_examine;
extern int wheel_lines;
extern int header_lines;
extern int def_search_type;
extern int updown_match;
extern lbool search_wrapped;
#if SHELL_ESCAPE || PIPEC
extern void *ml_shell;
#endif
#if EDITOR
extern char *editor;
extern char *editproto;
extern constant char *editproto;
#endif
#if OSC8_LINK
extern char *osc8_uri;
#endif
extern int screen_trashed; /* The screen has been overwritten */
extern int shift_count;
extern int oldbot;
extern int forw_prompt;
extern int incr_search;
extern int full_screen;
#if MSDOS_COMPILER==WIN32C
extern int utf_mode;
extern unsigned less_acp;
#endif
#if SHELL_ESCAPE
@ -83,11 +78,13 @@ static long fraction; /* The fractional part of the number */
static struct loption *curropt;
static int opt_lower;
static int optflag;
static int optgetname;
static lbool optgetname;
static POSITION bottompos;
static int save_hshift;
static int save_bs_mode;
static int save_proc_backspace;
static int screen_trashed_value = 0;
static lbool literal_char = FALSE;
#if PIPEC
static char pipec;
#endif
@ -95,11 +92,12 @@ static char pipec;
/* Stack of ungotten chars (via ungetcc) */
struct ungot {
struct ungot *ug_next;
LWCHAR ug_char;
char ug_char;
lbool ug_end_command;
};
static struct ungot* ungot = NULL;
static void multi_search (char *pattern, int n, int silent);
static void multi_search(constant char *pattern, int n, int silent);
/*
* Move the cursor to start of prompt line before executing a command.
@ -186,6 +184,8 @@ static void mca_search1(void)
cmd_putstr(buf);
}
}
if (literal_char)
cmd_putstr("Lit ");
#if HILITE_SEARCH
if (search_type & SRCH_FILTER)
@ -210,14 +210,10 @@ static void mca_search(void)
*/
static void mca_opt_toggle(void)
{
int no_prompt;
int flag;
char *dash;
int no_prompt = (optflag & OPT_NO_PROMPT);
int flag = (optflag & ~OPT_NO_PROMPT);
constant char *dash = (flag == OPT_NO_TOGGLE) ? "_" : "-";
no_prompt = (optflag & OPT_NO_PROMPT);
flag = (optflag & ~OPT_NO_PROMPT);
dash = (flag == OPT_NO_TOGGLE) ? "_" : "-";
set_mca(A_OPT_TOGGLE);
cmd_putstr(dash);
if (optgetname)
@ -242,7 +238,8 @@ static void mca_opt_toggle(void)
*/
static void exec_mca(void)
{
char *cbuf;
constant char *cbuf;
char *p;
cmd_exec();
cbuf = get_cmdbuf();
@ -286,9 +283,11 @@ static void exec_mca(void)
break;
#if EXAMINE
case A_EXAMINE:
if (secure)
if (!secure_allow(SF_EXAMINE))
break;
edit_list(cbuf);
p = save(cbuf);
edit_list(p);
free(p);
#if TAGS
/* If tag structure is loaded then clean it up. */
cleantags();
@ -296,39 +295,47 @@ static void exec_mca(void)
break;
#endif
#if SHELL_ESCAPE
case A_SHELL:
case A_SHELL: {
/*
* !! just uses whatever is in shellcmd.
* Otherwise, copy cmdbuf to shellcmd,
* expanding any special characters ("%" or "#").
*/
constant char *done_msg = (*cbuf == CONTROL('P')) ? NULL : "!done";
if (done_msg == NULL)
++cbuf;
if (*cbuf != '!')
{
if (shellcmd != NULL)
free(shellcmd);
shellcmd = fexpand(cbuf);
}
if (secure)
if (!secure_allow(SF_SHELL))
break;
if (shellcmd == NULL)
lsystem("", "!done");
else
lsystem(shellcmd, "!done");
break;
case A_PSHELL:
if (secure)
shellcmd = "";
lsystem(shellcmd, done_msg);
break; }
case A_PSHELL: {
constant char *done_msg = (*cbuf == CONTROL('P')) ? NULL : "#done";
if (done_msg == NULL)
++cbuf;
if (!secure_allow(SF_SHELL))
break;
lsystem(pr_expand(cbuf), "#done");
break;
lsystem(pr_expand(cbuf), done_msg);
break; }
#endif
#if PIPEC
case A_PIPE:
if (secure)
case A_PIPE: {
constant char *done_msg = (*cbuf == CONTROL('P')) ? NULL : "|done";
if (done_msg == NULL)
++cbuf;
if (!secure_allow(SF_PIPE))
break;
(void) pipe_mark(pipec, cbuf);
error("|done", NULL_PARG);
break;
if (done_msg != NULL)
error(done_msg, NULL_PARG);
break; }
#endif
}
}
@ -336,7 +343,7 @@ static void exec_mca(void)
/*
* Is a character an erase or kill char?
*/
static int is_erase_char(int c)
static lbool is_erase_char(char c)
{
return (c == erase_char || c == erase2_char || c == kill_char);
}
@ -344,7 +351,7 @@ static int is_erase_char(int c)
/*
* Is a character a carriage return or newline?
*/
static int is_newline_char(int c)
static lbool is_newline_char(char c)
{
return (c == '\n' || c == '\r');
}
@ -352,7 +359,7 @@ static int is_newline_char(int c)
/*
* Handle the first char of an option (after the initial dash).
*/
static int mca_opt_first_char(int c)
static int mca_opt_first_char(char c)
{
int no_prompt = (optflag & OPT_NO_PROMPT);
int flag = (optflag & ~OPT_NO_PROMPT);
@ -403,11 +410,11 @@ static int mca_opt_first_char(int c)
* If so, display the complete name and stop
* accepting chars until user hits RETURN.
*/
static int mca_opt_nonfirst_char(int c)
static int mca_opt_nonfirst_char(char c)
{
char *p;
char *oname;
int err;
constant char *p;
constant char *oname;
lbool ambig;
if (curropt != NULL)
{
@ -429,8 +436,7 @@ static int mca_opt_nonfirst_char(int c)
if (p == NULL)
return (MCA_MORE);
opt_lower = ASCII_IS_LOWER(p[0]);
err = 0;
curropt = findopt_name(&p, &oname, &err);
curropt = findopt_name(&p, &oname, &ambig);
if (curropt != NULL)
{
/*
@ -448,7 +454,7 @@ static int mca_opt_nonfirst_char(int c)
if (cmd_char(c) != CC_OK)
return (MCA_DONE);
}
} else if (err != OPT_AMBIG)
} else if (!ambig)
{
bell();
}
@ -458,7 +464,7 @@ static int mca_opt_nonfirst_char(int c)
/*
* Handle a char of an option toggle command.
*/
static int mca_opt_char(int c)
static int mca_opt_char(char c)
{
PARG parg;
@ -518,7 +524,7 @@ static int mca_opt_char(int c)
/*
* Display a prompt appropriate for the option parameter.
*/
start_mca(A_OPT_TOGGLE, opt_prompt(curropt), (void*)NULL, 0);
start_mca(A_OPT_TOGGLE, opt_prompt(curropt), NULL, 0);
return (MCA_MORE);
}
@ -536,7 +542,7 @@ public int norm_search_type(int st)
/*
* Handle a char of a search command.
*/
static int mca_search_char(int c)
static int mca_search_char(char c)
{
int flag = 0;
@ -547,8 +553,11 @@ static int mca_search_char(int c)
* * Toggle the PAST_EOF flag
* @ Toggle the FIRST_FILE flag
*/
if (len_cmdbuf() > 0)
if (len_cmdbuf() > 0 || literal_char)
{
literal_char = FALSE;
return (NO_MCA);
}
switch (c)
{
@ -594,6 +603,10 @@ static int mca_search_char(int c)
case '!':
flag = SRCH_NO_MATCH;
break;
case CONTROL('L'):
literal_char = TRUE;
flag = -1;
break;
}
if (flag != 0)
@ -609,7 +622,7 @@ static int mca_search_char(int c)
/*
* Handle a character of a multi-character command.
*/
static int mca_char(int c)
static int mca_char(char c)
{
int ret;
@ -718,7 +731,8 @@ static int mca_char(int c)
{
/* Incremental search: do a search after every input char. */
int st = (search_type & (SRCH_FORW|SRCH_BACK|SRCH_NO_MATCH|SRCH_NO_REGEX|SRCH_NO_MOVE|SRCH_WRAP|SRCH_SUBSEARCH_ALL));
char *pattern = get_cmdbuf();
ssize_t save_updown;
constant char *pattern = get_cmdbuf();
if (pattern == NULL)
return (MCA_MORE);
/*
@ -726,7 +740,7 @@ static int mca_char(int c)
* reinits it. That breaks history scrolling.
* {{ This is ugly. mca_search probably shouldn't call set_mlist. }}
*/
int save_updown_match = updown_match;
save_updown = save_updown_match();
cmd_exec();
if (*pattern == '\0')
{
@ -739,13 +753,13 @@ static int mca_char(int c)
undo_search(1);
}
/* Redraw the search prompt and search string. */
if (!full_screen)
if (is_screen_trashed() || !full_screen)
{
clear();
repaint();
}
mca_search1();
updown_match = save_updown_match;
restore_updown_match(save_updown);
cmd_repaint(NULL);
}
break;
@ -771,6 +785,21 @@ static void clear_buffers(void)
#endif
}
public void screen_trashed_num(int trashed)
{
screen_trashed_value = trashed;
}
public void screen_trashed(void)
{
screen_trashed_num(1);
}
public int is_screen_trashed(void)
{
return screen_trashed_value;
}
/*
* Make sure the screen is displayed.
*/
@ -791,13 +820,13 @@ static void make_display(void)
jump_loc(ch_zero(), 1);
else
jump_loc(initial_scrpos.pos, initial_scrpos.ln);
} else if (screen_trashed || !full_screen)
} else if (is_screen_trashed() || !full_screen)
{
int save_top_scroll = top_scroll;
int save_ignore_eoi = ignore_eoi;
top_scroll = 1;
ignore_eoi = 0;
if (screen_trashed == 2)
if (is_screen_trashed() == 2)
{
/* Special case used by ignore_eoi: re-open the input file
* and jump to the end of the file. */
@ -817,7 +846,7 @@ static void prompt(void)
{
constant char *p;
if (ungot != NULL && ungot->ug_char != CHAR_END_COMMAND)
if (ungot != NULL && !ungot->ug_end_command)
{
/*
* No prompt necessary if commands are from
@ -857,7 +886,7 @@ static void prompt(void)
{
WCHAR w[MAX_PATH+16];
p = pr_expand("Less?f - %f.");
MultiByteToWideChar(CP_ACP, 0, p, -1, w, sizeof(w)/sizeof(*w));
MultiByteToWideChar(less_acp, 0, p, -1, w, countof(w));
SetConsoleTitleW(w);
}
#endif
@ -883,6 +912,24 @@ static void prompt(void)
#if HILITE_SEARCH
if (is_filtering())
putstr("& ");
#endif
if (search_wrapped)
{
if (search_type & SRCH_BACK)
error("Search hit top; continuing at bottom", NULL_PARG);
else
error("Search hit bottom; continuing at top", NULL_PARG);
search_wrapped = FALSE;
}
#if OSC8_LINK
if (osc8_uri != NULL)
{
PARG parg;
parg.p_string = osc8_uri;
error("Link: %s", &parg);
free(osc8_uri);
osc8_uri = NULL;
}
#endif
if (p == NULL || *p == '\0')
{
@ -894,7 +941,7 @@ static void prompt(void)
#if MSDOS_COMPILER==WIN32C
WCHAR w[MAX_PATH*2];
char a[MAX_PATH*2];
MultiByteToWideChar(CP_ACP, 0, p, -1, w, sizeof(w)/sizeof(*w));
MultiByteToWideChar(less_acp, 0, p, -1, w, countof(w));
WideCharToMultiByte(utf_mode ? CP_UTF8 : GetConsoleOutputCP(),
0, w, -1, a, sizeof(a), NULL, NULL);
p = a;
@ -919,8 +966,9 @@ public void dispversion(void)
/*
* Return a character to complete a partial command, if possible.
*/
static LWCHAR getcc_end_command(void)
static char getcc_end_command(void)
{
int ch;
switch (mca)
{
case A_DIGIT:
@ -933,51 +981,76 @@ static LWCHAR getcc_end_command(void)
return ('\n');
default:
/* Some other incomplete command. Let user complete it. */
return ((ungot == NULL) ? getchr() : 0);
if (ungot != NULL)
return ('\0');
ch = getchr();
if (ch < 0) ch = '\0';
return (char) ch;
}
}
/*
* Get a command character from the ungotten stack.
*/
static char get_ungot(lbool *p_end_command)
{
struct ungot *ug = ungot;
char c = ug->ug_char;
if (p_end_command != NULL)
*p_end_command = ug->ug_end_command;
ungot = ug->ug_next;
free(ug);
return c;
}
/*
* Delete all ungotten characters.
*/
public void getcc_clear(void)
{
while (ungot != NULL)
(void) get_ungot(NULL);
}
/*
* Get command character.
* The character normally comes from the keyboard,
* but may come from ungotten characters
* (characters previously given to ungetcc or ungetsc).
*/
static LWCHAR getccu(void)
static char getccu(void)
{
LWCHAR c = 0;
while (c == 0)
int c = 0;
while (c == 0 && !ABORT_SIGS())
{
if (ungot == NULL)
{
/* Normal case: no ungotten chars.
* Get char from the user. */
c = getchr();
if (c < 0) return ('\0');
} else
{
/* Ungotten chars available:
* Take the top of stack (most recent). */
struct ungot *ug = ungot;
c = ug->ug_char;
ungot = ug->ug_next;
free(ug);
if (c == CHAR_END_COMMAND)
lbool end_command;
c = get_ungot(&end_command);
if (end_command)
c = getcc_end_command();
}
}
return (c);
return ((char) c);
}
/*
* Get a command character, but if we receive the orig sequence,
* convert it to the repl sequence.
*/
static LWCHAR getcc_repl(char constant *orig, char constant *repl, LWCHAR (*gr_getc)(void), void (*gr_ungetc)(LWCHAR))
static char getcc_repl(char constant *orig, char constant *repl, char (*gr_getc)(void), void (*gr_ungetc)(char))
{
LWCHAR c;
LWCHAR keys[16];
int ki = 0;
char c;
char keys[16];
size_t ki = 0;
c = (*gr_getc)();
if (orig == NULL || orig[0] == '\0')
@ -1012,7 +1085,7 @@ static LWCHAR getcc_repl(char constant *orig, char constant *repl, LWCHAR (*gr_g
/*
* Get command character.
*/
public int getcc(void)
public char getcc(void)
{
/* Replace kent (keypad Enter) with a newline. */
return getcc_repl(kent, "\n", getccu, ungetcc);
@ -1022,7 +1095,7 @@ public int getcc(void)
* "Unget" a command character.
* The next getcc() will return this character.
*/
public void ungetcc(LWCHAR c)
public void ungetcc(char c)
{
struct ungot *ug = (struct ungot *) ecalloc(1, sizeof(struct ungot));
@ -1035,10 +1108,11 @@ public void ungetcc(LWCHAR c)
* "Unget" a command character.
* If any other chars are already ungotten, put this one after those.
*/
public void ungetcc_back(LWCHAR c)
static void ungetcc_back1(char c, lbool end_command)
{
struct ungot *ug = (struct ungot *) ecalloc(1, sizeof(struct ungot));
ug->ug_char = c;
ug->ug_end_command = end_command;
ug->ug_next = NULL;
if (ungot == NULL)
ungot = ug;
@ -1051,11 +1125,21 @@ public void ungetcc_back(LWCHAR c)
}
}
public void ungetcc_back(char c)
{
ungetcc_back1(c, FALSE);
}
public void ungetcc_end_command(void)
{
ungetcc_back1('\0', TRUE);
}
/*
* Unget a whole string of command characters.
* The next sequence of getcc()'s will return this string.
*/
public void ungetsc(char *s)
public void ungetsc(constant char *s)
{
while (*s != '\0')
ungetcc_back(*s++);
@ -1064,9 +1148,9 @@ public void ungetsc(char *s)
/*
* Peek the next command character, without consuming it.
*/
public LWCHAR peekcc(void)
public char peekcc(void)
{
LWCHAR c = getcc();
char c = getcc();
ungetcc(c);
return c;
}
@ -1076,13 +1160,13 @@ public LWCHAR peekcc(void)
* If SRCH_FIRST_FILE is set, begin searching at the first file.
* If SRCH_PAST_EOF is set, continue the search thru multiple files.
*/
static void multi_search(char *pattern, int n, int silent)
static void multi_search(constant char *pattern, int n, int silent)
{
int nomore;
IFILE save_ifile;
int changed_file;
lbool changed_file;
changed_file = 0;
changed_file = FALSE;
save_ifile = save_curr_ifile();
if ((search_type & (SRCH_FORW|SRCH_BACK)) == 0)
@ -1102,7 +1186,7 @@ static void multi_search(char *pattern, int n, int silent)
unsave_ifile(save_ifile);
return;
}
changed_file = 1;
changed_file = TRUE;
search_type &= ~SRCH_FIRST_FILE;
}
@ -1147,7 +1231,7 @@ static void multi_search(char *pattern, int n, int silent)
nomore = edit_prev(1);
if (nomore)
break;
changed_file = 1;
changed_file = TRUE;
}
/*
@ -1213,18 +1297,18 @@ static int forw_loop(int until_hilite)
*/
public void commands(void)
{
int c;
char c;
int action;
char *cbuf;
constant char *cbuf;
constant char *msg;
int newaction;
int save_jump_sline;
int save_search_type;
char *extra;
char tbuf[2];
constant char *extra;
PARG parg;
IFILE old_ifile;
IFILE new_ifile;
char *tagfile;
constant char *tagfile;
search_type = SRCH_FORW;
wscroll = (sc_height + 1) / 2;
@ -1304,6 +1388,7 @@ public void commands(void)
/*
* Decode the command character and decide what to do.
*/
extra = NULL;
if (mca)
{
/*
@ -1318,6 +1403,7 @@ public void commands(void)
cbuf = get_cmdbuf();
if (cbuf == NULL)
continue;
action = fcmd_decode(cbuf, &extra);
} else
{
/*
@ -1328,12 +1414,9 @@ public void commands(void)
* want erase_char/kill_char to be treated
* as line editing characters.
*/
tbuf[0] = c;
tbuf[1] = '\0';
cbuf = tbuf;
constant char tbuf[2] = { c, '\0' };
action = fcmd_decode(tbuf, &extra);
}
extra = NULL;
action = fcmd_decode(cbuf, &extra);
/*
* If an "extra" string was returned,
* process it as a string of command characters.
@ -1355,7 +1438,7 @@ public void commands(void)
/*
* First digit of a number.
*/
start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE);
start_mca(A_DIGIT, ":", NULL, CF_QUIT_ON_ERASE);
goto again;
case A_F_WINDOW:
@ -1634,7 +1717,7 @@ public void commands(void)
if (number <= 0) number = 1; \
mca_search(); \
cmd_exec(); \
multi_search((char *)NULL, (int) number, 0);
multi_search(NULL, (int) number, 0);
case A_F_SEARCH:
/*
@ -1644,6 +1727,7 @@ public void commands(void)
search_type = SRCH_FORW | def_search_type;
if (number <= 0)
number = 1;
literal_char = FALSE;
mca_search();
c = getcc();
goto again;
@ -1656,13 +1740,58 @@ public void commands(void)
search_type = SRCH_BACK | def_search_type;
if (number <= 0)
number = 1;
literal_char = FALSE;
mca_search();
c = getcc();
goto again;
case A_OSC8_F_SEARCH:
#if OSC8_LINK
cmd_exec();
if (number <= 0)
number = 1;
osc8_search(SRCH_FORW, NULL, number);
#else
error("Command not available", NULL_PARG);
#endif
break;
case A_OSC8_B_SEARCH:
#if OSC8_LINK
cmd_exec();
if (number <= 0)
number = 1;
osc8_search(SRCH_BACK, NULL, number);
#else
error("Command not available", NULL_PARG);
#endif
break;
case A_OSC8_OPEN:
#if OSC8_LINK
if (secure_allow(SF_OSC8_OPEN))
{
cmd_exec();
osc8_open();
break;
}
#endif
error("Command not available", NULL_PARG);
break;
case A_OSC8_JUMP:
#if OSC8_LINK
cmd_exec();
osc8_jump();
#else
error("Command not available", NULL_PARG);
#endif
break;
case A_FILTER:
#if HILITE_SEARCH
search_type = SRCH_FORW | SRCH_FILTER;
literal_char = FALSE;
mca_search();
c = getcc();
goto again;
@ -1737,7 +1866,7 @@ public void commands(void)
* Edit a new file. Get the filename.
*/
#if EXAMINE
if (!secure)
if (secure_allow(SF_EXAMINE))
{
start_mca(A_EXAMINE, "Examine: ", ml_examine, 0);
c = getcc();
@ -1752,7 +1881,7 @@ public void commands(void)
* Invoke an editor on the input file.
*/
#if EDITOR
if (!secure)
if (secure_allow(SF_EDIT))
{
if (ch_getflags() & CH_HELPFILE)
break;
@ -1775,7 +1904,7 @@ public void commands(void)
*/
make_display();
cmd_exec();
lsystem(pr_expand(editproto), (char*)NULL);
lsystem(pr_expand(editproto), NULL);
break;
}
#endif
@ -1914,10 +2043,10 @@ public void commands(void)
optgetname = FALSE;
mca_opt_toggle();
c = getcc();
cbuf = opt_toggle_disallowed(c);
if (cbuf != NULL)
msg = opt_toggle_disallowed(c);
if (msg != NULL)
{
error(cbuf, NULL_PARG);
error(msg, NULL_PARG);
break;
}
goto again;
@ -1936,7 +2065,7 @@ public void commands(void)
/*
* Set an initial command for new files.
*/
start_mca(A_FIRSTCMD, "+", (void*)NULL, 0);
start_mca(A_FIRSTCMD, "+", NULL, 0);
c = getcc();
goto again;
@ -1946,7 +2075,7 @@ public void commands(void)
* Shell escape.
*/
#if SHELL_ESCAPE
if (!secure)
if (secure_allow(SF_SHELL))
{
start_mca(action, (action == A_SHELL) ? "!" : "#", ml_shell, 0);
c = getcc();
@ -1963,7 +2092,7 @@ public void commands(void)
*/
if (ch_getflags() & CH_HELPFILE)
break;
start_mca(A_SETMARK, "set mark: ", (void*)NULL, 0);
start_mca(A_SETMARK, "set mark: ", NULL, 0);
c = getcc();
if (is_erase_char(c) || is_newline_char(c))
break;
@ -1975,7 +2104,7 @@ public void commands(void)
/*
* Clear a mark.
*/
start_mca(A_CLRMARK, "clear mark: ", (void*)NULL, 0);
start_mca(A_CLRMARK, "clear mark: ", NULL, 0);
c = getcc();
if (is_erase_char(c) || is_newline_char(c))
break;
@ -1987,7 +2116,7 @@ public void commands(void)
/*
* Jump to a marked position.
*/
start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0);
start_mca(A_GOMARK, "goto mark: ", NULL, 0);
c = getcc();
if (is_erase_char(c) || is_newline_char(c))
break;
@ -2000,9 +2129,9 @@ public void commands(void)
* Write part of the input to a pipe to a shell command.
*/
#if PIPEC
if (!secure)
if (secure_allow(SF_PIPE))
{
start_mca(A_PIPE, "|mark: ", (void*)NULL, 0);
start_mca(A_PIPE, "|mark: ", NULL, 0);
c = getcc();
if (is_erase_char(c))
break;
@ -2021,7 +2150,7 @@ public void commands(void)
case A_B_BRACKET:
case A_F_BRACKET:
start_mca(action, "Brackets: ", (void*)NULL, 0);
start_mca(action, "Brackets: ", NULL, 0);
c = getcc();
goto again;
@ -2030,14 +2159,14 @@ public void commands(void)
* Shift view left.
*/
if (number > 0)
shift_count = number;
shift_count = (int) number;
else
number = (shift_count > 0) ?
shift_count : sc_width / 2;
number = (shift_count > 0) ? shift_count : sc_width / 2;
if (number > hshift)
number = hshift;
hshift -= number;
screen_trashed = 1;
pos_rehead();
hshift -= (int) number;
screen_trashed();
break;
case A_RSHIFT:
@ -2045,28 +2174,30 @@ public void commands(void)
* Shift view right.
*/
if (number > 0)
shift_count = number;
shift_count = (int) number;
else
number = (shift_count > 0) ?
shift_count : sc_width / 2;
hshift += number;
screen_trashed = 1;
number = (shift_count > 0) ? shift_count : sc_width / 2;
pos_rehead();
hshift += (int) number;
screen_trashed();
break;
case A_LLSHIFT:
/*
* Shift view left to margin.
*/
pos_rehead();
hshift = 0;
screen_trashed = 1;
screen_trashed();
break;
case A_RRSHIFT:
/*
* Shift view right to view rightmost char on screen.
*/
pos_rehead();
hshift = rrshift();
screen_trashed = 1;
screen_trashed();
break;
case A_PREFIX:
@ -2078,8 +2209,7 @@ public void commands(void)
if (mca != A_PREFIX)
{
cmd_reset();
start_mca(A_PREFIX, " ", (void*)NULL,
CF_QUIT_ON_ERASE);
start_mca(A_PREFIX, " ", NULL, CF_QUIT_ON_ERASE);
(void) cmd_char(c);
}
c = getcc();

View file

@ -1,4 +1,4 @@
/* Generated by "./mkutable -f2 Mn Me -- unicode/UnicodeData.txt" on Mon Nov 14 18:19:23 PST 2022 */
/* Generated by "./mkutable -f2 Mn Me -- unicode/UnicodeData.txt" on Sun Sep 17 17:56:27 PDT 2023 */
{ 0x0300, 0x036f }, /* Mn */
{ 0x0483, 0x0487 }, /* Mn */
{ 0x0488, 0x0489 }, /* Me */

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -19,8 +19,9 @@ extern int utf_mode;
/*
* Get the length of a buffer needed to convert a string.
*/
public int cvt_length(int len, int ops)
public size_t cvt_length(size_t len, int ops)
{
(void) ops;
if (utf_mode)
/*
* Just copying a string in UTF-8 mode can cause it to grow
@ -34,10 +35,10 @@ public int cvt_length(int len, int ops)
/*
* Allocate a chpos array for use by cvt_text.
*/
public int * cvt_alloc_chpos(int len)
public int * cvt_alloc_chpos(size_t len)
{
int i;
int *chpos = (int *) ecalloc(sizeof(int), len);
size_t i;
int *chpos = (int *) ecalloc(len, sizeof(int));
/* Initialize all entries to an invalid position. */
for (i = 0; i < len; i++)
chpos[i] = -1;
@ -49,12 +50,12 @@ public int * cvt_alloc_chpos(int len)
* Returns converted text in odst. The original offset of each
* odst character (when it was in osrc) is returned in the chpos array.
*/
public void cvt_text(char *odst, char *osrc, int *chpos, int *lenp, int ops)
public void cvt_text(mutable char *odst, constant char *osrc, mutable int *chpos, mutable size_t *lenp, int ops)
{
char *dst;
char *edst = odst;
char *src;
char *src_end;
constant char *src;
constant char *src_end;
LWCHAR ch;
if (lenp != NULL)
@ -64,10 +65,10 @@ public void cvt_text(char *odst, char *osrc, int *chpos, int *lenp, int ops)
for (src = osrc, dst = odst; src < src_end; )
{
int src_pos = (int) (src - osrc);
int dst_pos = (int) (dst - odst);
size_t src_pos = ptr_diff(src, osrc);
size_t dst_pos = ptr_diff(dst, odst);
struct ansi_state *pansi;
ch = step_char(&src, +1, src_end);
ch = step_charc(&src, +1, src_end);
if ((ops & CVT_BS) && ch == '\b' && dst > odst)
{
/* Delete backspace and preceding char. */
@ -82,18 +83,22 @@ public void cvt_text(char *odst, char *osrc, int *chpos, int *lenp, int ops)
{
if (ansi_step(pansi, ch) != ANSI_MID)
break;
ch = *src++;
ch = (LWCHAR) *src++; /* {{ would step_char work? }} */
}
ansi_done(pansi);
} else
{
/* Just copy the char to the destination buffer. */
char *cdst = dst;
if ((ops & CVT_TO_LC) && IS_UPPER(ch))
ch = TO_LOWER(ch);
put_wchar(&dst, ch);
/* Record the original position of the char. */
if (chpos != NULL)
chpos[dst_pos] = src_pos;
{
while (cdst++ < dst)
chpos[dst_pos++] = (int) src_pos; /*{{type-issue}}*/
}
}
if (dst > edst)
edst = dst;
@ -102,6 +107,6 @@ public void cvt_text(char *odst, char *osrc, int *chpos, int *lenp, int ops)
edst--;
*edst = '\0';
if (lenp != NULL)
*lenp = (int) (edst - odst);
*lenp = ptr_diff(edst, odst);
/* FIXME: why was this here? if (chpos != NULL) chpos[dst - odst] = src - osrc; */
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -34,11 +34,15 @@
#include "lesskey.h"
extern int erase_char, erase2_char, kill_char;
extern int secure;
extern int mousecap;
extern int screen_trashed;
extern int sc_height;
#if USERFILE
/* "content" is lesskey source, never binary. */
static void add_content_table(int (*call_lesskey)(constant char *, lbool), constant char *envname, lbool sysvar);
static int add_hometable(int (*call_lesskey)(constant char *, lbool), constant char *envname, constant char *def_filename, lbool sysvar);
#endif /* USERFILE */
#define SK(k) \
SK_SPECIAL_KEY, (k), 6, 1, 1, 1
/*
@ -96,8 +100,6 @@ static unsigned char cmdtable[] =
ESC,'<',0, A_GOLINE,
'p',0, A_PERCENT,
'%',0, A_PERCENT,
ESC,'[',0, A_LSHIFT,
ESC,']',0, A_RSHIFT,
ESC,'(',0, A_LSHIFT,
ESC,')',0, A_RSHIFT,
ESC,'{',0, A_LLSHIFT,
@ -155,6 +157,14 @@ static unsigned char cmdtable[] =
CONTROL('X'),CONTROL('V'),0, A_EXAMINE,
':','n',0, A_NEXT_FILE,
':','p',0, A_PREV_FILE,
CONTROL('O'),CONTROL('N'),0, A_OSC8_F_SEARCH,
CONTROL('O'),'n',0, A_OSC8_F_SEARCH,
CONTROL('O'),CONTROL('P'),0, A_OSC8_B_SEARCH,
CONTROL('O'),'p',0, A_OSC8_B_SEARCH,
CONTROL('O'),CONTROL('O'),0, A_OSC8_OPEN,
CONTROL('O'),'o',0, A_OSC8_OPEN,
CONTROL('O'),CONTROL('L'),0, A_OSC8_JUMP,
CONTROL('O'),'l',0, A_OSC8_JUMP,
't',0, A_NEXT_TAG,
'T',0, A_PREV_TAG,
':','x',0, A_INDEX_FILE,
@ -221,14 +231,27 @@ static unsigned char edittable[] =
ESC,'[','<',0, EC_X116MOUSE, /* X11 1006 mouse report */
};
static unsigned char dflt_vartable[] =
{
'L','E','S','S','_','O','S','C','8','_','m','a','n', 0, EV_OK|A_EXTRA,
/* echo '%o' | sed -e "s,^man\:\\([^(]*\\)( *\\([^)]*\\)\.*,-man '\\2' '\\1'," -e"t X" -e"s,\.*,-echo Invalid man link," -e"\: X" */
'e','c','h','o',' ','\'','%','o','\'',' ','|',' ','s','e','d',' ','-','e',' ','"','s',',','^','m','a','n','\\',':','\\','\\','(','[','^','(',']','*','\\','\\',')','(',' ','*','\\','\\','(','[','^',')',']','*','\\','\\',')','\\','.','*',',','-','m','a','n',' ','\'','\\','\\','2','\'',' ','\'','\\','\\','1','\'',',','"',' ','-','e','"','t',' ','X','"',' ','-','e','"','s',',','\\','.','*',',','-','e','c','h','o',' ','I','n','v','a','l','i','d',' ','m','a','n',' ','l','i','n','k',',','"',' ','-','e','"','\\',':',' ','X','"',
0,
'L','E','S','S','_','O','S','C','8','_','f','i','l','e', 0, EV_OK|A_EXTRA,
/* eval `echo '%o' | sed -e "s,^file://\\([^/]*\\)\\(.*\\),_H=\\1;_P=\\2;_E=0," -e"t X" -e"s,.*,_E=1," -e": X"`; if [ "$_E" = 1 ]; then echo -echo Invalid file link; elif [ -z "$_H" -o "$_H" = localhost -o "$_H" = $HOSTNAME ]; then echo ":e $_P"; else echo -echo Cannot open remote file on "$_H"; fi */
'e','v','a','l',' ','`','e','c','h','o',' ','\'','%','o','\'',' ','|',' ','s','e','d',' ','-','e',' ','"','s',',','^','f','i','l','e','\\',':','/','/','\\','\\','(','[','^','/',']','*','\\','\\',')','\\','\\','(','\\','.','*','\\','\\',')',',','_','H','=','\\','\\','1',';','_','P','=','\\','\\','2',';','_','E','=','0',',','"',' ','-','e','"','t',' ','X','"',' ','-','e','"','s',',','\\','.','*',',','_','E','=','1',',','"',' ','-','e','"','\\',':',' ','X','"','`',';',' ','i','f',' ','[',' ','"','$','_','E','"',' ','=',' ','1',' ',']',';',' ','t','h','e','n',' ','e','c','h','o',' ','-','e','c','h','o',' ','I','n','v','a','l','i','d',' ','f','i','l','e',' ','l','i','n','k',';',' ','e','l','i','f',' ','[',' ','-','z',' ','"','$','_','H','"',' ','-','o',' ','"','$','_','H','"',' ','=',' ','l','o','c','a','l','h','o','s','t',' ','-','o',' ','"','$','_','H','"',' ','=',' ','$','H','O','S','T','N','A','M','E',' ',']',';',' ','t','h','e','n',' ','e','c','h','o',' ','"','\\',':','e',' ','$','_','P','"',';',' ','e','l','s','e',' ','e','c','h','o',' ','-','e','c','h','o',' ','C','a','n','n','o','t',' ','o','p','e','n',' ','r','e','m','o','t','e',' ','f','i','l','e',' ','o','n',' ','"','$','_','H','"',';',' ','f','i',
0,
};
/*
* Structure to support a list of command tables.
*/
struct tablelist
{
struct tablelist *t_next;
char *t_start;
char *t_end;
unsigned char *t_start;
unsigned char *t_end;
};
/*
@ -243,13 +266,13 @@ static struct tablelist *list_sysvar_tables = NULL;
/*
* Expand special key abbreviations in a command table.
*/
static void expand_special_keys(char *table, int len)
static void expand_special_keys(unsigned char *table, size_t len)
{
char *fm;
char *to;
unsigned char *fm;
unsigned char *to;
int a;
char *repl;
int klen;
constant char *repl;
size_t klen;
for (fm = table; fm < table + len; )
{
@ -276,10 +299,10 @@ static void expand_special_keys(char *table, int len)
repl = special_key_str(fm[1]);
klen = fm[2] & 0377;
fm += klen;
if (repl == NULL || (int) strlen(repl) > klen)
if (repl == NULL || strlen(repl) > klen)
repl = "\377";
while (*repl != '\0')
*to++ = *repl++;
*to++ = (unsigned char) *repl++; /*{{type-issue}}*/
}
*to++ = '\0';
/*
@ -306,7 +329,7 @@ static void expand_cmd_table(struct tablelist *tlist)
struct tablelist *t;
for (t = tlist; t != NULL; t = t->t_next)
{
expand_special_keys(t->t_start, t->t_end - t->t_start);
expand_special_keys(t->t_start, ptr_diff(t->t_end, t->t_start));
}
}
@ -321,7 +344,6 @@ public void expand_cmd_tables(void)
expand_cmd_table(list_sysvar_tables);
}
/*
* Initialize the command lists.
*/
@ -330,12 +352,13 @@ public void init_cmds(void)
/*
* Add the default command tables.
*/
add_fcmd_table((char*)cmdtable, sizeof(cmdtable));
add_ecmd_table((char*)edittable, sizeof(edittable));
add_fcmd_table(cmdtable, sizeof(cmdtable));
add_ecmd_table(edittable, sizeof(edittable));
add_sysvar_table(dflt_vartable, sizeof(dflt_vartable));
#if USERFILE
#ifdef BINDIR /* For backwards compatibility */
/* Try to add tables in the OLD system lesskey file. */
add_hometable(lesskey, NULL, BINDIR "/.sysless", 1);
add_hometable(lesskey, NULL, BINDIR "/.sysless", TRUE);
#endif
/*
* Try to load lesskey source file or binary file.
@ -348,33 +371,36 @@ public void init_cmds(void)
* Try to add tables in system lesskey src file.
*/
#if HAVE_LESSKEYSRC
if (add_hometable(lesskey_src, "LESSKEYIN_SYSTEM", LESSKEYINFILE_SYS, 1) != 0)
if (add_hometable(lesskey_src, "LESSKEYIN_SYSTEM", LESSKEYINFILE_SYS, TRUE) != 0)
#endif
{
/*
* Try to add the tables in the system lesskey binary file.
*/
add_hometable(lesskey, "LESSKEY_SYSTEM", LESSKEYFILE_SYS, 1);
add_hometable(lesskey, "LESSKEY_SYSTEM", LESSKEYFILE_SYS, TRUE);
}
/*
* Try to add tables in the lesskey src file "$HOME/.lesskey".
*/
#if HAVE_LESSKEYSRC
if (add_hometable(lesskey_src, "LESSKEYIN", DEF_LESSKEYINFILE, 0) != 0)
if (add_hometable(lesskey_src, "LESSKEYIN", DEF_LESSKEYINFILE, FALSE) != 0)
#endif
{
/*
* Try to add the tables in the standard lesskey binary file "$HOME/.less".
*/
add_hometable(lesskey, "LESSKEY", LESSKEYFILE, 0);
add_hometable(lesskey, "LESSKEY", LESSKEYFILE, FALSE);
}
#endif
add_content_table(lesskey_content, "LESSKEY_CONTENT_SYSTEM", TRUE);
add_content_table(lesskey_content, "LESSKEY_CONTENT", FALSE);
#endif /* USERFILE */
}
/*
* Add a command table.
*/
static int add_cmd_table(struct tablelist **tlist, char *buf, int len)
static int add_cmd_table(struct tablelist **tlist, unsigned char *buf, size_t len)
{
struct tablelist *t;
@ -391,15 +417,46 @@ static int add_cmd_table(struct tablelist **tlist, char *buf, int len)
}
t->t_start = buf;
t->t_end = buf + len;
t->t_next = *tlist;
*tlist = t;
t->t_next = NULL;
if (*tlist == NULL)
*tlist = t;
else
{
struct tablelist *e;
for (e = *tlist; e->t_next != NULL; e = e->t_next)
continue;
e->t_next = t;
}
return (0);
}
/*
* Remove the last command table in a list.
*/
static void pop_cmd_table(struct tablelist **tlist)
{
struct tablelist *t;
if (*tlist == NULL)
return;
if ((*tlist)->t_next == NULL)
{
t = *tlist;
*tlist = NULL;
} else
{
struct tablelist *e;
for (e = *tlist; e->t_next->t_next != NULL; e = e->t_next)
continue;
t = e->t_next;
e->t_next = NULL;
}
free(t);
}
/*
* Add a command table.
*/
public void add_fcmd_table(char *buf, int len)
public void add_fcmd_table(unsigned char *buf, size_t len)
{
if (add_cmd_table(&list_fcmd_tables, buf, len) < 0)
error("Warning: some commands disabled", NULL_PARG);
@ -408,7 +465,7 @@ public void add_fcmd_table(char *buf, int len)
/*
* Add an editing command table.
*/
public void add_ecmd_table(char *buf, int len)
public void add_ecmd_table(unsigned char *buf, size_t len)
{
if (add_cmd_table(&list_ecmd_tables, buf, len) < 0)
error("Warning: some edit commands disabled", NULL_PARG);
@ -417,12 +474,27 @@ public void add_ecmd_table(char *buf, int len)
/*
* Add an environment variable table.
*/
static void add_var_table(struct tablelist **tlist, char *buf, int len)
static void add_var_table(struct tablelist **tlist, unsigned char *buf, size_t len)
{
if (add_cmd_table(tlist, buf, len) < 0)
struct xbuffer xbuf;
xbuf_init(&xbuf);
expand_evars((char*)buf, len, &xbuf); /*{{unsigned-issue}}*/
/* {{ We leak the table in buf. expand_evars scribbled in it so it's useless anyway. }} */
if (add_cmd_table(tlist, xbuf.data, xbuf.end) < 0)
error("Warning: environment variables from lesskey file unavailable", NULL_PARG);
}
public void add_uvar_table(unsigned char *buf, size_t len)
{
add_var_table(&list_var_tables, buf, len);
}
public void add_sysvar_table(unsigned char *buf, size_t len)
{
add_var_table(&list_sysvar_tables, buf, len);
}
/*
* Return action for a mouse wheel down event.
*/
@ -440,18 +512,42 @@ static int mouse_wheel_up(void)
}
/*
* Return action for a mouse button release event.
* Return action for the left mouse button trigger.
*/
static int mouse_button_rel(int x, int y)
static int mouse_button_left(int x, int y)
{
/*
* {{ It would be better to return an action and then do this
* in commands() but it's nontrivial to pass y to it. }}
*/
#if OSC8_LINK
if (osc8_click(y, x))
return (A_NOACTION);
#else
(void) x;
#endif /* OSC8_LINK */
if (y < sc_height-1)
{
setmark('#', y);
screen_trashed = 1;
screen_trashed();
}
return (A_NOACTION);
}
/*
* Return action for the right mouse button trigger.
*/
static int mouse_button_right(int x, int y)
{
(void) x;
/*
* {{ unlike mouse_button_left, we could return an action,
* but keep it near mouse_button_left for readability. }}
*/
if (y < sc_height-1)
{
gomark('#');
screen_trashed();
}
return (A_NOACTION);
}
@ -485,6 +581,7 @@ static int getcc_int(char *pterm)
*/
static int x11mouse_action(int skip)
{
static int prev_b = X11MOUSE_BUTTON_REL;
int b = getcc() - X11MOUSE_OFFSET;
int x = getcc() - X11MOUSE_OFFSET-1;
int y = getcc() - X11MOUSE_OFFSET-1;
@ -492,13 +589,23 @@ static int x11mouse_action(int skip)
return (A_NOACTION);
switch (b) {
default:
prev_b = b;
return (A_NOACTION);
case X11MOUSE_WHEEL_DOWN:
return mouse_wheel_down();
case X11MOUSE_WHEEL_UP:
return mouse_wheel_up();
case X11MOUSE_BUTTON_REL:
return mouse_button_rel(x, y);
/* to trigger on button-up, we check the last button-down */
switch (prev_b) {
case X11MOUSE_BUTTON1:
return mouse_button_left(x, y);
/* is BUTTON2 the rightmost with 2-buttons mouse? */
case X11MOUSE_BUTTON2:
case X11MOUSE_BUTTON3:
return mouse_button_right(x, y);
}
return (A_NOACTION);
}
}
@ -523,20 +630,24 @@ static int x116mouse_action(int skip)
return mouse_wheel_down();
case X11MOUSE_WHEEL_UP:
return mouse_wheel_up();
case X11MOUSE_BUTTON1:
if (ch != 'm') return (A_NOACTION);
return mouse_button_left(x, y);
default:
if (ch != 'm') return (A_NOACTION);
return mouse_button_rel(x, y);
/* any other button release */
return mouse_button_right(x, y);
}
}
/*
* Search a single command table for the command string in cmd.
*/
static int cmd_search(char *cmd, char *table, char *endtable, char **sp)
static int cmd_search(constant char *cmd, constant char *table, constant char *endtable, constant char **sp)
{
char *p;
char *q;
int a;
constant char *p;
constant char *q;
int a = A_INVALID;
*sp = NULL;
for (p = table, q = cmd; p < endtable; p++, q++)
@ -571,13 +682,15 @@ static int cmd_search(char *cmd, char *table, char *endtable, char **sp)
if (a & A_EXTRA)
{
*sp = ++p;
while (*p != '\0')
++p;
a &= ~A_EXTRA;
}
if (a == A_X11MOUSE_IN)
a = x11mouse_action(0);
else if (a == A_X116MOUSE_IN)
a = x116mouse_action(0);
return (a);
q = cmd-1;
}
} else if (*q == '\0')
{
@ -586,7 +699,9 @@ static int cmd_search(char *cmd, char *table, char *endtable, char **sp)
* but not the end of the string in the command table.
* The user's command is incomplete.
*/
return (A_PREFIX);
if (a == A_INVALID)
a = A_PREFIX;
q = cmd-1;
} else
{
/*
@ -600,8 +715,10 @@ static int cmd_search(char *cmd, char *table, char *endtable, char **sp)
/*
* A_END_LIST is a special marker that tells
* us to abort the cmd search.
* Negative action means accept this action
* without searching any more cmd tables.
*/
return (A_UINVALID);
return -a;
}
while (*p++ != '\0')
continue;
@ -613,40 +730,44 @@ static int cmd_search(char *cmd, char *table, char *endtable, char **sp)
q = cmd-1;
}
}
/*
* No match found in the entire command table.
*/
return (A_INVALID);
return (a);
}
/*
* Decode a command character and return the associated action.
* The "extra" string, if any, is returned in sp.
*/
static int cmd_decode(struct tablelist *tlist, char *cmd, char **sp)
static int cmd_decode(struct tablelist *tlist, constant char *cmd, constant char **sp)
{
struct tablelist *t;
int action = A_INVALID;
/*
* Search thru all the command tables.
* Stop when we find an action which is not A_INVALID.
* Search for the cmd thru all the command tables.
* If we find it more than once, take the last one.
*/
*sp = NULL;
for (t = tlist; t != NULL; t = t->t_next)
{
action = cmd_search(cmd, t->t_start, t->t_end, sp);
if (action != A_INVALID)
break;
constant char *tsp;
int taction = cmd_search(cmd, (char *) t->t_start, (char *) t->t_end, &tsp);
if (taction == A_UINVALID)
taction = A_INVALID;
if (taction != A_INVALID)
{
*sp = tsp;
if (taction < 0)
return (-taction);
action = taction;
}
}
if (action == A_UINVALID)
action = A_INVALID;
return (action);
}
/*
* Decode a command from the cmdtables list.
*/
public int fcmd_decode(char *cmd, char **sp)
public int fcmd_decode(constant char *cmd, constant char **sp)
{
return (cmd_decode(list_fcmd_tables, cmd, sp));
}
@ -654,7 +775,7 @@ public int fcmd_decode(char *cmd, char **sp)
/*
* Decode a command from the edittables list.
*/
public int ecmd_decode(char *cmd, char **sp)
public int ecmd_decode(constant char *cmd, constant char **sp)
{
return (cmd_decode(list_ecmd_tables, cmd, sp));
}
@ -663,10 +784,10 @@ public int ecmd_decode(char *cmd, char **sp)
* Get the value of an environment variable.
* Looks first in the lesskey file, then in the real environment.
*/
public char * lgetenv(char *var)
public constant char * lgetenv(constant char *var)
{
int a;
char *s;
constant char *s;
a = cmd_decode(list_var_tables, var, &s);
if (a == EV_OK)
@ -680,10 +801,42 @@ public char * lgetenv(char *var)
return (NULL);
}
/*
* Like lgetenv, but also uses a buffer partially filled with an env table.
*/
public constant char * lgetenv_ext(constant char *var, unsigned char *env_buf, size_t env_buf_len)
{
constant char *r;
size_t e;
size_t env_end = 0;
for (e = 0;;)
{
for (; e < env_buf_len; e++)
if (env_buf[e] == '\0')
break;
if (e >= env_buf_len) break;
if (env_buf[++e] & A_EXTRA)
{
for (e = e+1; e < env_buf_len; e++)
if (env_buf[e] == '\0')
break;
}
e++;
if (e >= env_buf_len) break;
env_end = e;
}
/* Temporarily add env_buf to var_tables, do the lookup, then remove it. */
add_uvar_table(env_buf, env_end);
r = lgetenv(var);
pop_cmd_table(&list_var_tables);
return r;
}
/*
* Is a string null or empty?
*/
public int isnullenv(char *s)
public lbool isnullenv(constant char *s)
{
return (s == NULL || *s == '\0');
}
@ -694,9 +847,9 @@ public int isnullenv(char *s)
* Integers are stored in a funny format:
* two bytes, low order first, in radix KRADIX.
*/
static int gint(char **sp)
static size_t gint(unsigned char **sp)
{
int n;
size_t n;
n = *(*sp)++;
n += *(*sp)++ * KRADIX;
@ -706,7 +859,7 @@ static int gint(char **sp)
/*
* Process an old (pre-v241) lesskey file.
*/
static int old_lesskey(char *buf, int len)
static int old_lesskey(unsigned char *buf, size_t len)
{
/*
* Old-style lesskey file.
@ -724,12 +877,12 @@ static int old_lesskey(char *buf, int len)
/*
* Process a new (post-v241) lesskey file.
*/
static int new_lesskey(char *buf, int len, int sysvar)
static int new_lesskey(unsigned char *buf, size_t len, lbool sysvar)
{
char *p;
char *end;
unsigned char *p;
unsigned char *end;
int c;
int n;
size_t n;
/*
* New-style lesskey file.
@ -748,24 +901,26 @@ static int new_lesskey(char *buf, int len, int sysvar)
{
case CMD_SECTION:
n = gint(&p);
if (n < 0 || p+n >= end)
if (p+n >= end)
return (-1);
add_fcmd_table(p, n);
p += n;
break;
case EDIT_SECTION:
n = gint(&p);
if (n < 0 || p+n >= end)
if (p+n >= end)
return (-1);
add_ecmd_table(p, n);
p += n;
break;
case VAR_SECTION:
n = gint(&p);
if (n < 0 || p+n >= end)
if (p+n >= end)
return (-1);
add_var_table((sysvar) ?
&list_sysvar_tables : &list_var_tables, p, n);
if (sysvar)
add_sysvar_table(p, n);
else
add_uvar_table(p, n);
p += n;
break;
case END_SECTION:
@ -782,14 +937,14 @@ static int new_lesskey(char *buf, int len, int sysvar)
/*
* Set up a user command table, based on a "lesskey" file.
*/
public int lesskey(char *filename, int sysvar)
public int lesskey(constant char *filename, lbool sysvar)
{
char *buf;
unsigned char *buf;
POSITION len;
long n;
ssize_t n;
int f;
if (secure)
if (!secure_allow(SF_LESSKEY))
return (1);
/*
* Try to open the lesskey file.
@ -815,18 +970,18 @@ public int lesskey(char *filename, int sysvar)
close(f);
return (-1);
}
if ((buf = (char *) calloc((int)len, sizeof(char))) == NULL)
if ((buf = (unsigned char *) calloc((size_t)len, sizeof(char))) == NULL)
{
close(f);
return (-1);
}
if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK)
if (less_lseek(f, (less_off_t)0, SEEK_SET) == BAD_LSEEK)
{
free(buf);
close(f);
return (-1);
}
n = read(f, buf, (unsigned int) len);
n = read(f, buf, (size_t) len);
close(f);
if (n != len)
{
@ -841,24 +996,38 @@ public int lesskey(char *filename, int sysvar)
if (len < 4 ||
buf[0] != C0_LESSKEY_MAGIC || buf[1] != C1_LESSKEY_MAGIC ||
buf[2] != C2_LESSKEY_MAGIC || buf[3] != C3_LESSKEY_MAGIC)
return (old_lesskey(buf, (int)len));
return (new_lesskey(buf, (int)len, sysvar));
return (old_lesskey(buf, (size_t) len));
return (new_lesskey(buf, (size_t) len, sysvar));
}
#if HAVE_LESSKEYSRC
public int lesskey_src(char *filename, int sysvar)
static int lesskey_text(constant char *filename, lbool sysvar, lbool content)
{
static struct lesskey_tables tables;
int r = parse_lesskey(filename, &tables);
if (!secure_allow(SF_LESSKEY))
return (1);
int r = content ? parse_lesskey_content(filename, &tables) : parse_lesskey(filename, &tables);
if (r != 0)
return (r);
add_fcmd_table(xbuf_char_data(&tables.cmdtable.buf), tables.cmdtable.buf.end);
add_ecmd_table(xbuf_char_data(&tables.edittable.buf), tables.edittable.buf.end);
add_var_table(sysvar ? &list_sysvar_tables : &list_var_tables,
xbuf_char_data(&tables.vartable.buf), tables.vartable.buf.end);
add_fcmd_table(tables.cmdtable.buf.data, tables.cmdtable.buf.end);
add_ecmd_table(tables.edittable.buf.data, tables.edittable.buf.end);
if (sysvar)
add_sysvar_table(tables.vartable.buf.data, tables.vartable.buf.end);
else
add_uvar_table(tables.vartable.buf.data, tables.vartable.buf.end);
return (0);
}
public int lesskey_src(constant char *filename, lbool sysvar)
{
return lesskey_text(filename, sysvar, FALSE);
}
public int lesskey_content(constant char *content, lbool sysvar)
{
return lesskey_text(content, sysvar, TRUE);
}
void lesskey_parse_error(char *s)
{
PARG parg;
@ -870,24 +1039,25 @@ void lesskey_parse_error(char *s)
/*
* Add a lesskey file.
*/
public int add_hometable(int (*call_lesskey)(char *, int), char *envname, char *def_filename, int sysvar)
static int add_hometable(int (*call_lesskey)(constant char *, lbool), constant char *envname, constant char *def_filename, lbool sysvar)
{
char *filename;
char *filename = NULL;
constant char *efilename;
int r;
if (envname != NULL && (filename = lgetenv(envname)) != NULL)
filename = save(filename);
if (envname != NULL && (efilename = lgetenv(envname)) != NULL)
filename = save(efilename);
else if (sysvar) /* def_filename is full path */
filename = save(def_filename);
else /* def_filename is just basename */
{
/* Remove first char (normally a dot) unless stored in $HOME. */
char *xdg = lgetenv("XDG_CONFIG_HOME");
constant char *xdg = lgetenv("XDG_CONFIG_HOME");
if (!isnullenv(xdg))
filename = dirfile(xdg, &def_filename[1], 1);
if (filename == NULL)
{
char *home = lgetenv("HOME");
constant char *home = lgetenv("HOME");
if (!isnullenv(home))
{
char *cfg_dir = dirfile(home, ".config", 0);
@ -904,16 +1074,28 @@ public int add_hometable(int (*call_lesskey)(char *, int), char *envname, char *
free(filename);
return (r);
}
#endif
/*
* Add the content of a lesskey source file.
*/
static void add_content_table(int (*call_lesskey)(constant char *, lbool), constant char *envname, lbool sysvar)
{
(void) call_lesskey; /* not used */
constant char *content = lgetenv(envname);
if (isnullenv(content))
return;
lesskey_content(content, sysvar);
}
#endif /* USERFILE */
/*
* See if a char is a special line-editing command.
*/
public int editchar(int c, int flags)
public int editchar(char c, int flags)
{
int action;
int nch;
char *s;
constant char *s;
char usercmd[MAX_CMDLEN+1];
/*
@ -939,7 +1121,7 @@ public int editchar(int c, int flags)
*/
nch = 0;
do {
if (nch > 0)
if (nch > 0)
c = getcc();
usercmd[nch] = c;
usercmd[nch+1] = '\0';

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -16,12 +16,13 @@
#if HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#if OS2 || __MVS__ || (defined WIFSIGNALED && defined WTERMSIG)
#include <signal.h>
#endif
public int fd0 = 0;
extern int new_file;
extern int cbufs;
extern lbool new_file;
extern char *every_first_cmd;
extern int force_open;
extern int is_tty;
@ -58,14 +59,14 @@ public ino_t curr_ino;
* words, returning each one as a standard null-terminated string.
* back_textlist does the same, but runs thru the list backwards.
*/
public void init_textlist(struct textlist *tlist, char *str)
public void init_textlist(struct textlist *tlist, mutable char *str)
{
char *s;
#if SPACES_IN_FILENAMES
int meta_quoted = 0;
int delim_quoted = 0;
char *esc = get_meta_escape();
int esclen = (int) strlen(esc);
lbool meta_quoted = FALSE;
lbool delim_quoted = FALSE;
constant char *esc = get_meta_escape();
size_t esclen = strlen(esc);
#endif
tlist->string = skipsp(str);
@ -99,9 +100,9 @@ public void init_textlist(struct textlist *tlist, char *str)
}
}
public char * forw_textlist(struct textlist *tlist, char *prev)
public constant char * forw_textlist(struct textlist *tlist, constant char *prev)
{
char *s;
constant char *s;
/*
* prev == NULL means return the first word in the list.
@ -120,9 +121,9 @@ public char * forw_textlist(struct textlist *tlist, char *prev)
return (s);
}
public char * back_textlist(struct textlist *tlist, char *prev)
public constant char * back_textlist(struct textlist *tlist, constant char *prev)
{
char *s;
constant char *s;
/*
* prev == NULL means return the last word in the list.
@ -146,9 +147,9 @@ public char * back_textlist(struct textlist *tlist, char *prev)
/*
* Parse a single option setting in a modeline.
*/
static void modeline_option(char *str, int opt_len)
static void modeline_option(constant char *str, size_t opt_len)
{
struct mloption { char *opt_name; void (*opt_func)(char*,int); };
struct mloption { constant char *opt_name; void (*opt_func)(constant char*,size_t); };
struct mloption options[] = {
{ "ts=", set_tabs },
{ "tabstop=", set_tabs },
@ -157,7 +158,7 @@ static void modeline_option(char *str, int opt_len)
struct mloption *opt;
for (opt = options; opt->opt_name != NULL; opt++)
{
int name_len = strlen(opt->opt_name);
size_t name_len = strlen(opt->opt_name);
if (opt_len > name_len && strncmp(str, opt->opt_name, name_len) == 0)
{
(*opt->opt_func)(str + name_len, opt_len - name_len);
@ -170,10 +171,10 @@ static void modeline_option(char *str, int opt_len)
* String length, terminated by option separator (space or colon).
* Space/colon can be escaped with backspace.
*/
static int modeline_option_len(char *str)
static size_t modeline_option_len(constant char *str)
{
int esc = FALSE;
char *s;
lbool esc = FALSE;
constant char *s;
for (s = str; *s != '\0'; s++)
{
if (esc)
@ -183,18 +184,18 @@ static int modeline_option_len(char *str)
else if (*s == ' ' || *s == ':') /* separator */
break;
}
return (s - str);
return ptr_diff(s, str);
}
/*
* Parse colon- or space-separated option settings in a modeline.
*/
static void modeline_options(char *str, char end_char)
static void modeline_options(constant char *str, char end_char)
{
for (;;)
{
int opt_len;
str = skipsp(str);
size_t opt_len;
str = skipspc(str);
if (*str == '\0' || *str == end_char)
break;
opt_len = modeline_option_len(str);
@ -208,21 +209,21 @@ static void modeline_options(char *str, char end_char)
/*
* See if there is a modeline string in a line.
*/
static void check_modeline(char *line)
static void check_modeline(constant char *line)
{
#if HAVE_STRSTR
static char *pgms[] = { "less:", "vim:", "vi:", "ex:", NULL };
char **pgm;
static constant char *pgms[] = { "less:", "vim:", "vi:", "ex:", NULL };
constant char **pgm;
for (pgm = pgms; *pgm != NULL; ++pgm)
{
char *pline = line;
constant char *pline = line;
for (;;)
{
char *str;
constant char *str;
pline = strstr(pline, *pgm);
if (pline == NULL) /* pgm is not in this line */
break;
str = skipsp(pline + strlen(*pgm));
str = skipspc(pline + strlen(*pgm));
if (pline == line || pline[-1] == ' ')
{
if (strncmp(str, "set ", 4) == 0)
@ -247,8 +248,8 @@ static void check_modelines(void)
int i;
for (i = 0; i < modelines; i++)
{
char *line;
int line_len;
constant char *line;
size_t line_len;
if (ABORT_SIGS())
return;
pos = forw_raw_line(pos, &line, &line_len);
@ -264,6 +265,7 @@ static void check_modelines(void)
static void close_pipe(FILE *pipefd)
{
int status;
char *p;
PARG parg;
if (pipefd == NULL)
@ -279,9 +281,10 @@ static void close_pipe(FILE *pipefd)
if (status == -1)
{
/* An internal error in 'less', not a preprocessor error. */
parg.p_string = errno_message("pclose");
p = errno_message("pclose");
parg.p_string = p;
error("%s", &parg);
free(parg.p_string);
free(p);
return;
}
if (!show_preproc_error)
@ -298,7 +301,7 @@ static void close_pipe(FILE *pipefd)
return;
}
#endif
#if defined WIFSIGNALED && defined WTERMSIG && HAVE_STRSIGNAL
#if defined WIFSIGNALED && defined WTERMSIG
if (WIFSIGNALED(status))
{
int sig = WTERMSIG(status);
@ -348,7 +351,7 @@ public void check_altpipe_error(void)
static void close_file(void)
{
struct scrpos scrpos;
char *altfilename;
constant char *altfilename;
if (curr_ifile == NULL_IFILE)
return;
@ -389,7 +392,7 @@ static void close_file(void)
* Filename == "-" means standard input.
* Filename == NULL means just close the current file.
*/
public int edit(char *filename)
public int edit(constant char *filename)
{
if (filename == NULL)
return (edit_ifile(NULL_IFILE));
@ -399,20 +402,19 @@ public int edit(char *filename)
/*
* Clean up what edit_ifile did before error return.
*/
static int edit_error(char *filename, char *alt_filename, void *altpipe, IFILE ifile, IFILE was_curr_ifile)
static int edit_error(constant char *filename, constant char *alt_filename, void *altpipe, IFILE ifile)
{
if (alt_filename != NULL)
{
close_pipe(altpipe);
close_altfile(alt_filename, filename);
free(alt_filename);
free((char*)alt_filename); /* FIXME: WTF? */
}
del_ifile(ifile);
free(filename);
/*
* Re-open the current file.
*/
if (was_curr_ifile == ifile)
if (curr_ifile == ifile)
{
/*
* Whoops. The "current" ifile is the one we just deleted.
@ -420,7 +422,6 @@ static int edit_error(char *filename, char *alt_filename, void *altpipe, IFILE i
*/
quit(QUIT_ERROR);
}
reedit_ifile(was_curr_ifile);
return (1);
}
@ -433,13 +434,15 @@ public int edit_ifile(IFILE ifile)
int f;
int answer;
int chflags;
char *filename;
char *open_filename;
constant char *filename;
constant char *open_filename;
char *alt_filename;
void *altpipe;
IFILE was_curr_ifile;
char *p;
PARG parg;
ssize_t nread = 0;
if (ifile == curr_ifile)
{
/*
@ -447,31 +450,143 @@ public int edit_ifile(IFILE ifile)
*/
return (0);
}
new_file = TRUE;
if (ifile != NULL_IFILE)
{
/*
* See if LESSOPEN specifies an "alternate" file to open.
*/
filename = get_filename(ifile);
altpipe = get_altpipe(ifile);
if (altpipe != NULL)
{
/*
* File is already open.
* chflags and f are not used by ch_init if ifile has
* filestate which should be the case if we're here.
* Set them here to avoid uninitialized variable warnings.
*/
chflags = 0;
f = -1;
alt_filename = get_altfilename(ifile);
open_filename = (alt_filename != NULL) ? alt_filename : filename;
} else
{
if (strcmp(filename, FAKE_HELPFILE) == 0 ||
strcmp(filename, FAKE_EMPTYFILE) == 0)
alt_filename = NULL;
else
alt_filename = open_altfile(filename, &f, &altpipe);
open_filename = (alt_filename != NULL) ? alt_filename : filename;
chflags = 0;
if (altpipe != NULL)
{
/*
* The alternate "file" is actually a pipe.
* f has already been set to the file descriptor of the pipe
* in the call to open_altfile above.
* Keep the file descriptor open because it was opened
* via popen(), and pclose() wants to close it.
*/
chflags |= CH_POPENED;
if (strcmp(filename, "-") == 0)
chflags |= CH_KEEPOPEN;
} else if (strcmp(filename, "-") == 0)
{
/*
* Use standard input.
* Keep the file descriptor open because we can't reopen it.
*/
f = fd0;
chflags |= CH_KEEPOPEN;
/*
* Must switch stdin to BINARY mode.
*/
SET_BINARY(f);
#if MSDOS_COMPILER==DJGPPC
/*
* Setting stdin to binary by default causes
* Ctrl-C to not raise SIGINT. We must undo
* that side-effect.
*/
__djgpp_set_ctrl_c(1);
#endif
} else if (strcmp(open_filename, FAKE_EMPTYFILE) == 0)
{
f = -1;
chflags |= CH_NODATA;
} else if (strcmp(open_filename, FAKE_HELPFILE) == 0)
{
f = -1;
chflags |= CH_HELPFILE;
} else if ((p = bad_file(open_filename)) != NULL)
{
/*
* It looks like a bad file. Don't try to open it.
*/
parg.p_string = p;
error("%s", &parg);
free(p);
return edit_error(filename, alt_filename, altpipe, ifile);
} else if ((f = open(open_filename, OPEN_READ)) < 0)
{
/*
* Got an error trying to open it.
*/
char *p = errno_message(filename);
parg.p_string = p;
error("%s", &parg);
free(p);
return edit_error(filename, alt_filename, altpipe, ifile);
} else
{
chflags |= CH_CANSEEK;
if (bin_file(f, &nread) && !force_open && !opened(ifile))
{
/*
* Looks like a binary file.
* Ask user if we should proceed.
*/
parg.p_string = filename;
answer = query("\"%s\" may be a binary file. See it anyway? ", &parg);
if (answer != 'y' && answer != 'Y')
{
close(f);
return edit_error(filename, alt_filename, altpipe, ifile);
}
}
}
}
if (!force_open && f >= 0 && isatty(f))
{
PARG parg;
parg.p_string = filename;
error("%s is a terminal (use -f to open it)", &parg);
return edit_error(filename, alt_filename, altpipe, ifile);
}
}
/*
* We must close the currently open file now.
* This is necessary to make the open_altfile/close_altfile pairs
* nest properly (or rather to avoid nesting at all).
* {{ Some stupid implementations of popen() mess up if you do:
* fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }}
*/
#if LOGFILE
end_logfile();
#endif
was_curr_ifile = save_curr_ifile();
if (curr_ifile != NULL_IFILE)
{
chflags = ch_getflags();
int was_helpfile = (ch_getflags() & CH_HELPFILE);
close_file();
if ((chflags & CH_HELPFILE) && held_ifile(was_curr_ifile) <= 1)
if (was_helpfile && held_ifile(was_curr_ifile) <= 1)
{
/*
* Don't keep the help file in the ifile list.
*/
del_ifile(was_curr_ifile);
was_curr_ifile = old_ifile;
was_curr_ifile = NULL_IFILE;
}
}
unsave_ifile(was_curr_ifile);
if (ifile == NULL_IFILE)
{
@ -481,145 +596,26 @@ public int edit_ifile(IFILE ifile)
* you're supposed to have saved curr_ifile yourself,
* and you'll restore it if necessary.)
*/
unsave_ifile(was_curr_ifile);
return (0);
}
filename = save(get_filename(ifile));
/*
* See if LESSOPEN specifies an "alternate" file to open.
*/
altpipe = get_altpipe(ifile);
if (altpipe != NULL)
{
/*
* File is already open.
* chflags and f are not used by ch_init if ifile has
* filestate which should be the case if we're here.
* Set them here to avoid uninitialized variable warnings.
*/
chflags = 0;
f = -1;
alt_filename = get_altfilename(ifile);
open_filename = (alt_filename != NULL) ? alt_filename : filename;
} else
{
if (strcmp(filename, FAKE_HELPFILE) == 0 ||
strcmp(filename, FAKE_EMPTYFILE) == 0)
alt_filename = NULL;
else
alt_filename = open_altfile(filename, &f, &altpipe);
open_filename = (alt_filename != NULL) ? alt_filename : filename;
chflags = 0;
if (altpipe != NULL)
{
/*
* The alternate "file" is actually a pipe.
* f has already been set to the file descriptor of the pipe
* in the call to open_altfile above.
* Keep the file descriptor open because it was opened
* via popen(), and pclose() wants to close it.
*/
chflags |= CH_POPENED;
if (strcmp(filename, "-") == 0)
chflags |= CH_KEEPOPEN;
} else if (strcmp(filename, "-") == 0)
{
/*
* Use standard input.
* Keep the file descriptor open because we can't reopen it.
*/
f = fd0;
chflags |= CH_KEEPOPEN;
/*
* Must switch stdin to BINARY mode.
*/
SET_BINARY(f);
#if MSDOS_COMPILER==DJGPPC
/*
* Setting stdin to binary by default causes
* Ctrl-C to not raise SIGINT. We must undo
* that side-effect.
*/
__djgpp_set_ctrl_c(1);
#endif
} else if (strcmp(open_filename, FAKE_EMPTYFILE) == 0)
{
f = -1;
chflags |= CH_NODATA;
} else if (strcmp(open_filename, FAKE_HELPFILE) == 0)
{
f = -1;
chflags |= CH_HELPFILE;
} else if ((parg.p_string = bad_file(open_filename)) != NULL)
{
/*
* It looks like a bad file. Don't try to open it.
*/
error("%s", &parg);
free(parg.p_string);
return edit_error(filename, alt_filename, altpipe, ifile, was_curr_ifile);
} else if ((f = open(open_filename, OPEN_READ)) < 0)
{
/*
* Got an error trying to open it.
*/
parg.p_string = errno_message(filename);
error("%s", &parg);
free(parg.p_string);
return edit_error(filename, alt_filename, altpipe, ifile, was_curr_ifile);
} else
{
chflags |= CH_CANSEEK;
if (!force_open && !opened(ifile) && bin_file(f))
{
/*
* Looks like a binary file.
* Ask user if we should proceed.
*/
parg.p_string = filename;
answer = query("\"%s\" may be a binary file. See it anyway? ",
&parg);
if (answer != 'y' && answer != 'Y')
{
close(f);
return edit_error(filename, alt_filename, altpipe, ifile, was_curr_ifile);
}
}
}
}
if (!force_open && f >= 0 && isatty(f))
{
PARG parg;
parg.p_string = filename;
error("%s is a terminal (use -f to open it)", &parg);
return edit_error(filename, alt_filename, altpipe, ifile, was_curr_ifile);
}
/*
* Get the new ifile.
* Set up the new ifile.
* Get the saved position for the file.
*/
if (was_curr_ifile != NULL_IFILE)
{
old_ifile = was_curr_ifile;
unsave_ifile(was_curr_ifile);
}
curr_ifile = ifile;
set_altfilename(curr_ifile, alt_filename);
set_altpipe(curr_ifile, altpipe);
set_open(curr_ifile); /* File has been opened */
get_pos(curr_ifile, &initial_scrpos);
new_file = TRUE;
ch_init(f, chflags);
ch_init(f, chflags, nread);
consecutive_nulls = 0;
check_modelines();
if (!(chflags & CH_HELPFILE))
{
if (was_curr_ifile != NULL_IFILE)
old_ifile = was_curr_ifile;
#if LOGFILE
if (namelogfile != NULL && is_tty)
use_logfile(namelogfile);
@ -640,7 +636,7 @@ public int edit_ifile(IFILE ifile)
if (every_first_cmd != NULL)
{
ungetsc(every_first_cmd);
ungetcc_back(CHAR_END_COMMAND);
ungetcc_end_command();
}
}
@ -669,8 +665,8 @@ public int edit_ifile(IFILE ifile)
}
if (want_filesize)
scan_eof();
set_header(ch_zero());
}
free(filename);
return (0);
}
@ -682,10 +678,10 @@ public int edit_ifile(IFILE ifile)
public int edit_list(char *filelist)
{
IFILE save_ifile;
char *good_filename;
char *filename;
constant char *good_filename;
constant char *filename;
char *gfilelist;
char *gfilename;
constant char *gfilename;
char *qfilename;
struct textlist tl_files;
struct textlist tl_gfiles;
@ -929,7 +925,7 @@ public void cat_file(void)
* is standard input, create the log file.
* We take care not to blindly overwrite an existing file.
*/
public void use_logfile(char *filename)
public void use_logfile(constant char *filename)
{
int exists;
int answer;
@ -982,7 +978,7 @@ loop:
* Append: open the file and seek to the end.
*/
logfile = open(filename, OPEN_APPEND);
if (lseek(logfile, (off_t)0, SEEK_END) == BAD_LSEEK)
if (less_lseek(logfile, (less_off_t)0, SEEK_END) == BAD_LSEEK)
{
close(logfile);
logfile = -1;

192
contrib/less/evar.c Normal file
View file

@ -0,0 +1,192 @@
/*
* Copyright (C) 1984-2024 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.
*/
/*
* Code to support expanding environment variables in text.
*/
#include "less.h"
#include "xbuf.h"
struct replace {
struct replace *r_next;
char *r_fm;
char *r_to;
};
/*
* Skip to the next unescaped slash or right curly bracket in a string.
*/
static size_t skipsl(constant char *buf, size_t len, size_t e)
{
lbool esc = FALSE;
while (e < len && buf[e] != '\0' && (esc || (buf[e] != '/' && buf[e] != '}')))
{
esc = (!esc && buf[e] == '\\');
++e;
}
return e;
}
/*
* Parse a replacement string: one or more instances of
* (slash, pattern, slash, replacement), followed by right curly bracket.
* Replacement may be empty in which case the second slash is optional.
*/
static struct replace * make_replaces(mutable char *buf, size_t len, size_t *pe, char term)
{
size_t e = *pe;
struct replace *replaces = NULL;
while (term == '/')
{
struct replace *repl;
size_t to;
size_t fm = e;
e = skipsl(buf, len, e);
if (e >= len) break;
if (e == fm) /* missing fm string; we're done */
{
while (e < len)
if (buf[e++] == '}') break;
break;
}
term = buf[e];
buf[e++] = '\0'; /* terminate the fm string */
if (term != '/') /* missing to string */
{
to = e-1;
} else
{
to = e;
e = skipsl(buf, len, e);
if (e >= len) break;
term = buf[e];
buf[e++] = '\0'; /* terminate the to string */
}
repl = ecalloc(1, sizeof(struct replace));
repl->r_fm = &buf[fm];
repl->r_to = &buf[to];
repl->r_next = replaces;
replaces = repl;
}
*pe = e;
return replaces;
}
/*
* Free a list of replace structs.
*/
static void free_replaces(struct replace *replaces)
{
while (replaces != NULL)
{
struct replace *r = replaces;
replaces = r->r_next;
free(r);
}
}
/*
* See if the initial substring of a string matches a pattern.
* Backslash escapes in the pattern are ignored.
* Return the length of the matched substring, or 0 if no match.
*/
static size_t evar_match(constant char *str, constant char *pat)
{
size_t len = 0;
while (*pat != '\0')
{
if (*pat == '\\') ++pat;
if (*str++ != *pat++) return 0;
++len;
}
return len;
}
/*
* Find the replacement for a string (&evar[*pv]),
* given a list of replace structs.
*/
static constant char * find_replace(constant struct replace *repl, constant char *evar, size_t *pv)
{
for (; repl != NULL; repl = repl->r_next)
{
size_t len = evar_match(&evar[*pv], repl->r_fm);
if (len > 0)
{
*pv += len;
return repl->r_to;
}
}
return NULL;
}
/*
* With buf[e] positioned just after NAME in "${NAME" and
* term containing the character at that point, parse the rest
* of the environment var string (including the final right curly bracket).
* Write evar to xbuf, performing any specified text replacements.
* Return the new value of e to point just after the final right curly bracket.
*/
static size_t add_evar(struct xbuffer *xbuf, mutable char *buf, size_t len, size_t e, constant char *evar, char term)
{
struct replace *replaces = make_replaces(buf, len, &e, term);
size_t v;
for (v = 0; evar[v] != '\0'; )
{
constant char *repl = find_replace(replaces, evar, &v);
if (repl == NULL)
xbuf_add_char(xbuf, evar[v++]);
else
{
size_t r;
for (r = 0; repl[r] != '\0'; r++)
{
if (repl[r] == '\\') ++r;
xbuf_add_char(xbuf, repl[r]);
}
}
}
free_replaces(replaces);
return e;
}
/*
* Expand env variables in a string.
* Writes expanded output to xbuf. Corrupts buf.
*/
public void expand_evars(mutable char *buf, size_t len, struct xbuffer *xbuf)
{
size_t i;
for (i = 0; i < len; )
{
if (i+1 < len && buf[i] == '$' && buf[i+1] == '{')
{
constant char *evar;
char term;
size_t e;
i += 2; /* skip "${" */
for (e = i; e < len; e++)
if (buf[e] == '\0' || buf[e] == '}' || buf[e] == '/')
break;
if (e >= len || buf[e] == '\0')
break; /* missing right curly bracket; ignore var */
term = buf[e];
buf[e++] = '\0';
evar = lgetenv_ext(&buf[i], xbuf->data, xbuf->end);
if (evar == NULL) evar = "";
i = add_evar(xbuf, buf, len, e, evar, term);
} else
{
xbuf_add_char(xbuf, buf[i++]);
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -44,7 +44,6 @@
#endif
extern int force_open;
extern int secure;
extern int use_lessopen;
extern int ctldisp;
extern int utf_mode;
@ -62,7 +61,7 @@ extern dev_t curr_dev;
/*
* Remove quotes around a filename.
*/
public char * shell_unquote(char *str)
public char * shell_unquote(constant char *str)
{
char *name;
char *p;
@ -83,8 +82,8 @@ public char * shell_unquote(char *str)
}
} else
{
char *esc = get_meta_escape();
int esclen = (int) strlen(esc);
constant char *esc = get_meta_escape();
size_t esclen = strlen(esc);
while (*str != '\0')
{
if (esclen > 0 && strncmp(str, esc, esclen) == 0)
@ -99,9 +98,9 @@ public char * shell_unquote(char *str)
/*
* Get the shell's escape character.
*/
public char * get_meta_escape(void)
public constant char * get_meta_escape(void)
{
char *s;
constant char *s;
s = lgetenv("LESSMETAESCAPE");
if (s == NULL)
@ -112,9 +111,9 @@ public char * get_meta_escape(void)
/*
* Get the characters which the shell considers to be "metacharacters".
*/
static char * metachars(void)
static constant char * metachars(void)
{
static char *mchars = NULL;
static constant char *mchars = NULL;
if (mchars == NULL)
{
@ -128,33 +127,43 @@ static char * metachars(void)
/*
* Is this a shell metacharacter?
*/
static int metachar(char c)
static lbool metachar(char c)
{
return (strchr(metachars(), c) != NULL);
}
/*
* Must use quotes rather than escape char for this metachar?
*/
static lbool must_quote(char c)
{
/* {{ Maybe the set of must_quote chars should be configurable? }} */
return (c == '\n');
}
/*
* Insert a backslash before each metacharacter in a string.
*/
public char * shell_quote(char *s)
public char * shell_quoten(constant char *s, size_t slen)
{
char *p;
constant char *p;
char *np;
char *newstr;
int len;
char *esc = get_meta_escape();
int esclen = (int) strlen(esc);
int use_quotes = 0;
int have_quotes = 0;
size_t len;
constant char *esc = get_meta_escape();
size_t esclen = strlen(esc);
lbool use_quotes = FALSE;
lbool have_quotes = FALSE;
/*
* Determine how big a string we need to allocate.
*/
len = 1; /* Trailing null byte */
for (p = s; *p != '\0'; p++)
for (p = s; p < s + slen; p++)
{
len++;
if (*p == openquote || *p == closequote)
have_quotes = 1;
have_quotes = TRUE;
if (metachar(*p))
{
if (esclen == 0)
@ -163,7 +172,10 @@ public char * shell_quote(char *s)
* We've got a metachar, but this shell
* doesn't support escape chars. Use quotes.
*/
use_quotes = 1;
use_quotes = TRUE;
} else if (must_quote(*p))
{
len += 3; /* open quote + char + close quote */
} else
{
/*
@ -180,42 +192,55 @@ public char * shell_quote(char *s)
* We can't quote a string that contains quotes.
*/
return (NULL);
len = (int) strlen(s) + 3;
len = slen + 3;
}
/*
* Allocate and construct the new string.
*/
newstr = p = (char *) ecalloc(len, sizeof(char));
newstr = np = (char *) ecalloc(len, sizeof(char));
if (use_quotes)
{
SNPRINTF3(newstr, len, "%c%s%c", openquote, s, closequote);
SNPRINTF4(newstr, len, "%c%.*s%c", openquote, (int) slen, s, closequote);
} else
{
while (*s != '\0')
constant char *es = s + slen;
while (s < es)
{
if (metachar(*s))
if (!metachar(*s))
{
/*
* Add the escape char.
*/
strcpy(p, esc);
p += esclen;
*np++ = *s++;
} else if (must_quote(*s))
{
/* Surround the char with quotes. */
*np++ = openquote;
*np++ = *s++;
*np++ = closequote;
} else
{
/* Insert an escape char before the char. */
strcpy(np, esc);
np += esclen;
*np++ = *s++;
}
*p++ = *s++;
}
*p = '\0';
*np = '\0';
}
return (newstr);
}
public char * shell_quote(constant char *s)
{
return shell_quoten(s, strlen(s));
}
/*
* Return a pathname that points to a specified file in a specified directory.
* Return NULL if the file does not exist in the directory.
*/
public char * dirfile(char *dirname, char *filename, int must_exist)
public char * dirfile(constant char *dirname, constant char *filename, int must_exist)
{
char *pathname;
int len;
size_t len;
int f;
if (dirname == NULL || *dirname == '\0')
@ -223,7 +248,7 @@ public char * dirfile(char *dirname, char *filename, int must_exist)
/*
* Construct the full pathname.
*/
len = (int) (strlen(dirname) + strlen(filename) + 2);
len = strlen(dirname) + strlen(filename) + 2;
pathname = (char *) calloc(len, sizeof(char));
if (pathname == NULL)
return (NULL);
@ -249,7 +274,7 @@ public char * dirfile(char *dirname, char *filename, int must_exist)
/*
* Return the full pathname of the given file in the "home directory".
*/
public char * homefile(char *filename)
public char * homefile(constant char *filename)
{
char *pathname;
@ -284,6 +309,69 @@ public char * homefile(char *filename)
return (NULL);
}
typedef struct xcpy { char *dest; size_t copied; } xcpy;
static void xcpy_char(xcpy *xp, char ch)
{
if (xp->dest != NULL) *(xp->dest)++ = ch;
xp->copied++;
}
static void xcpy_filename(xcpy *xp, constant char *str)
{
/* If filename contains spaces, quote it
* to prevent edit_list from splitting it. */
lbool quote = (strchr(str, ' ') != NULL);
if (quote)
xcpy_char(xp, openquote);
for (; *str != '\0'; str++)
xcpy_char(xp, *str);
if (quote)
xcpy_char(xp, closequote);
}
static size_t fexpand_copy(constant char *fr, char *to)
{
xcpy xp;
xp.copied = 0;
xp.dest = to;
for (; *fr != '\0'; fr++)
{
lbool expand = FALSE;
switch (*fr)
{
case '%':
case '#':
if (fr[1] == *fr)
{
/* Two identical chars. Output just one. */
fr += 1;
} else
{
/* Single char. Expand to a (quoted) file name. */
expand = TRUE;
}
break;
default:
break;
}
if (expand)
{
IFILE ifile = (*fr == '%') ? curr_ifile : (*fr == '#') ? old_ifile : NULL_IFILE;
if (ifile == NULL_IFILE)
xcpy_char(&xp, *fr);
else
xcpy_filename(&xp, get_filename(ifile));
} else
{
xcpy_char(&xp, *fr);
}
}
xcpy_char(&xp, '\0');
return xp.copied;
}
/*
* Expand a string, substituting any "%" with the current filename,
* and any "#" with the previous filename.
@ -291,90 +379,22 @@ public char * homefile(char *filename)
* Likewise for a string of N "#"s.
* {{ This is a lot of work just to support % and #. }}
*/
public char * fexpand(char *s)
public char * fexpand(constant char *s)
{
char *fr, *to;
int n;
size_t n;
char *e;
IFILE ifile;
#define fchar_ifile(c) \
((c) == '%' ? curr_ifile : \
(c) == '#' ? old_ifile : NULL_IFILE)
/*
* Make one pass to see how big a buffer we
* need to allocate for the expanded string.
*/
n = 0;
for (fr = s; *fr != '\0'; fr++)
{
switch (*fr)
{
case '%':
case '#':
if (fr > s && fr[-1] == *fr)
{
/*
* Second (or later) char in a string
* of identical chars. Treat as normal.
*/
n++;
} else if (fr[1] != *fr)
{
/*
* Single char (not repeated). Treat specially.
*/
ifile = fchar_ifile(*fr);
if (ifile == NULL_IFILE)
n++;
else
n += (int) strlen(get_filename(ifile));
}
/*
* Else it is the first char in a string of
* identical chars. Just discard it.
*/
break;
default:
n++;
break;
}
}
e = (char *) ecalloc(n+1, sizeof(char));
n = fexpand_copy(s, NULL);
e = (char *) ecalloc(n, sizeof(char));
/*
* Now copy the string, expanding any "%" or "#".
*/
to = e;
for (fr = s; *fr != '\0'; fr++)
{
switch (*fr)
{
case '%':
case '#':
if (fr > s && fr[-1] == *fr)
{
*to++ = *fr;
} else if (fr[1] != *fr)
{
ifile = fchar_ifile(*fr);
if (ifile == NULL_IFILE)
*to++ = *fr;
else
{
strcpy(to, get_filename(ifile));
to += strlen(to);
}
}
break;
default:
*to++ = *fr;
break;
}
}
*to = '\0';
fexpand_copy(s, e);
return (e);
}
@ -385,12 +405,14 @@ public char * fexpand(char *s)
* Return a blank-separated list of filenames which "complete"
* the given string.
*/
public char * fcomplete(char *s)
public char * fcomplete(constant char *s)
{
char *fpat;
char *qs;
char *uqs;
if (secure)
/* {{ Is this needed? lglob calls secure_allow. }} */
if (!secure_allow(SF_GLOB))
return (NULL);
/*
* Complete the filename "s" by globbing "s*".
@ -404,12 +426,12 @@ public char * fcomplete(char *s)
* but "FILE.A" is globbed as "FILE.A*").
*/
{
char *slash;
int len;
constant char *slash;
size_t len;
for (slash = s+strlen(s)-1; slash > s; slash--)
if (*slash == *PATHNAME_SEP || *slash == '/')
break;
len = (int) strlen(s) + 4;
len = strlen(s) + 4;
fpat = (char *) ecalloc(len, sizeof(char));
if (strchr(slash, '.') == NULL)
SNPRINTF1(fpat, len, "%s*.*", s);
@ -418,14 +440,14 @@ public char * fcomplete(char *s)
}
#else
{
int len = (int) strlen(s) + 2;
size_t len = strlen(s) + 2;
fpat = (char *) ecalloc(len, sizeof(char));
SNPRINTF1(fpat, len, "%s*", s);
}
#endif
qs = lglob(fpat);
s = shell_unquote(qs);
if (strcmp(s,fpat) == 0)
uqs = shell_unquote(qs);
if (strcmp(uqs, fpat) == 0)
{
/*
* The filename didn't expand.
@ -433,7 +455,7 @@ public char * fcomplete(char *s)
free(qs);
qs = NULL;
}
free(s);
free(uqs);
free(fpat);
return (qs);
}
@ -442,32 +464,35 @@ public char * fcomplete(char *s)
/*
* Try to determine if a file is "binary".
* This is just a guess, and we need not try too hard to make it accurate.
*
* The number of bytes read is returned to the caller, because it will
* be used later to compare to st_size from stat(2) to see if the file
* is lying about its size.
*/
public int bin_file(int f)
public int bin_file(int f, ssize_t *n)
{
int n;
int bin_count = 0;
char data[256];
char* p;
char* edata;
constant char* p;
constant char* edata;
if (!seekable(f))
return (0);
if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK)
if (less_lseek(f, (less_off_t)0, SEEK_SET) == BAD_LSEEK)
return (0);
n = read(f, data, sizeof(data));
if (n <= 0)
*n = read(f, data, sizeof(data));
if (*n <= 0)
return (0);
edata = &data[n];
edata = &data[*n];
for (p = data; p < edata; )
{
if (utf_mode && !is_utf8_well_formed(p, edata-p))
if (utf_mode && !is_utf8_well_formed(p, (int) ptr_diff(edata,p)))
{
bin_count++;
utf_skip_to_lead(&p, edata);
} else
{
LWCHAR c = step_char(&p, +1, edata);
LWCHAR c = step_charc(&p, +1, edata);
struct ansi_state *pansi;
if (ctldisp == OPT_ONPLUS && (pansi = ansi_start(c)) != NULL)
{
@ -489,9 +514,9 @@ public int bin_file(int f)
*/
static POSITION seek_filesize(int f)
{
off_t spos;
less_off_t spos;
spos = lseek(f, (off_t)0, SEEK_END);
spos = less_lseek(f, (less_off_t)0, SEEK_END);
if (spos == BAD_LSEEK)
return (NULL_POSITION);
return ((POSITION) spos);
@ -502,53 +527,31 @@ static POSITION seek_filesize(int f)
* Read a string from a file.
* Return a pointer to the string in memory.
*/
static char * readfd(FILE *fd)
public char * readfd(FILE *fd)
{
int len;
int ch;
char *buf;
char *p;
/*
* Make a guess about how many chars in the string
* and allocate a buffer to hold it.
*/
len = 100;
buf = (char *) ecalloc(len, sizeof(char));
for (p = buf; ; p++)
struct xbuffer xbuf;
xbuf_init(&xbuf);
for (;;)
{
int ch;
if ((ch = getc(fd)) == '\n' || ch == EOF)
break;
if (p - buf >= len-1)
{
/*
* The string is too big to fit in the buffer we have.
* Allocate a new buffer, twice as big.
*/
len *= 2;
*p = '\0';
p = (char *) ecalloc(len, sizeof(char));
strcpy(p, buf);
free(buf);
buf = p;
p = buf + strlen(buf);
}
*p = ch;
xbuf_add_char(&xbuf, (char) ch);
}
*p = '\0';
return (buf);
xbuf_add_char(&xbuf, '\0');
return (char *) xbuf.data;
}
/*
* Execute a shell command.
* Return a pointer to a pipe connected to the shell command's standard output.
*/
static FILE * shellcmd(char *cmd)
static FILE * shellcmd(constant char *cmd)
{
FILE *fd;
#if HAVE_SHELL
char *shell;
constant char *shell;
shell = lgetenv("SHELL");
if (!isnullenv(shell))
@ -566,7 +569,7 @@ static FILE * shellcmd(char *cmd)
fd = popen(cmd, "r");
} else
{
int len = (int) (strlen(shell) + strlen(esccmd) + 5);
size_t len = strlen(shell) + strlen(esccmd) + 5;
scmd = (char *) ecalloc(len, sizeof(char));
SNPRINTF3(scmd, len, "%s %s %s", shell, shell_coption(), esccmd);
free(esccmd);
@ -592,12 +595,12 @@ static FILE * shellcmd(char *cmd)
/*
* Expand a filename, doing any system-specific metacharacter substitutions.
*/
public char * lglob(char *filename)
public char * lglob(constant char *afilename)
{
char *gfilename;
char *filename = fexpand(afilename);
filename = fexpand(filename);
if (secure)
if (!secure_allow(SF_GLOB))
return (filename);
#ifdef DECL_GLOB_LIST
@ -605,7 +608,7 @@ public char * lglob(char *filename)
/*
* The globbing function returns a list of names.
*/
int length;
size_t length;
char *p;
char *qfilename;
DECL_GLOB_LIST(list)
@ -651,8 +654,8 @@ public char * lglob(char *filename)
* is called multiple times to walk thru all names.
*/
char *p;
int len;
int n;
size_t len;
size_t n;
char *pfilename;
char *qfilename;
DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle)
@ -668,14 +671,14 @@ public char * lglob(char *filename)
gfilename = (char *) ecalloc(len, sizeof(char));
p = gfilename;
do {
n = (int) (strlen(drive) + strlen(dir) + strlen(fnd.GLOB_NAME) + 1);
n = strlen(drive) + strlen(dir) + strlen(fnd.GLOB_NAME) + 1;
pfilename = (char *) ecalloc(n, sizeof(char));
SNPRINTF3(pfilename, n, "%s%s%s", drive, dir, fnd.GLOB_NAME);
qfilename = shell_quote(pfilename);
free(pfilename);
if (qfilename != NULL)
{
n = (int) strlen(qfilename);
n = strlen(qfilename);
while (p - gfilename + n + 2 >= len)
{
/*
@ -711,17 +714,18 @@ public char * lglob(char *filename)
* an "echo" command to the shell and reading its output.
*/
FILE *fd;
char *s;
char *lessecho;
constant char *s;
constant char *lessecho;
char *cmd;
char *esc;
int len;
constant char *esc;
char *qesc;
size_t len;
esc = get_meta_escape();
if (strlen(esc) == 0)
esc = "-";
esc = shell_quote(esc);
if (esc == NULL)
qesc = shell_quote(esc);
if (qesc == NULL)
{
return (filename);
}
@ -731,11 +735,11 @@ public char * lglob(char *filename)
/*
* Invoke lessecho, and read its output (a globbed list of filenames).
*/
len = (int) (strlen(lessecho) + strlen(filename) + (7*strlen(metachars())) + 24);
len = strlen(lessecho) + strlen(filename) + (7*strlen(metachars())) + 24;
cmd = (char *) ecalloc(len, sizeof(char));
SNPRINTF4(cmd, len, "%s -p0x%x -d0x%x -e%s ", lessecho,
(unsigned char) openquote, (unsigned char) closequote, esc);
free(esc);
(unsigned char) openquote, (unsigned char) closequote, qesc);
free(qesc);
for (s = metachars(); *s != '\0'; s++)
sprintf(cmd + strlen(cmd), "-n0x%x ", (unsigned char) *s);
sprintf(cmd + strlen(cmd), "-- %s", filename);
@ -772,7 +776,7 @@ public char * lglob(char *filename)
/*
* Does path not represent something in the file system?
*/
public int is_fake_pathname(char *path)
public lbool is_fake_pathname(constant char *path)
{
return (strcmp(path, "-") == 0 ||
strcmp(path, FAKE_HELPFILE) == 0 || strcmp(path, FAKE_EMPTYFILE) == 0);
@ -781,14 +785,29 @@ public int is_fake_pathname(char *path)
/*
* Return canonical pathname.
*/
public char * lrealpath(char *path)
public char * lrealpath(constant char *path)
{
if (!is_fake_pathname(path))
{
#if HAVE_REALPATH
/*
* Not all systems support the POSIX.1-2008 realpath() behavior
* of allocating when passing a NULL argument. And PATH_MAX is
* not required to be defined, or might contain an exceedingly
* big value. We assume that if it is not defined (such as on
* GNU/Hurd), then realpath() accepts NULL.
*/
#ifndef PATH_MAX
char *rpath;
rpath = realpath(path, NULL);
if (rpath != NULL)
return (rpath);
#else
char rpath[PATH_MAX];
if (realpath(path, rpath) != NULL)
return (save(rpath));
#endif
#endif
}
return (save(path));
@ -799,7 +818,7 @@ public char * lrealpath(char *path)
* Return number of %s escapes in a string.
* Return a large number if there are any other % escapes besides %s.
*/
static int num_pct_s(char *lessopen)
static int num_pct_s(constant char *lessopen)
{
int num = 0;
@ -824,21 +843,23 @@ static int num_pct_s(char *lessopen)
* See if we should open a "replacement file"
* instead of the file we're about to open.
*/
public char * open_altfile(char *filename, int *pf, void **pfd)
public char * open_altfile(constant char *filename, int *pf, void **pfd)
{
#if !HAVE_POPEN
return (NULL);
#else
char *lessopen;
constant char *lessopen;
char *qfilename;
char *cmd;
int len;
size_t len;
FILE *fd;
#if HAVE_FILENO
int returnfd = 0;
#endif
if (!use_lessopen || secure)
if (!secure_allow(SF_LESSOPEN))
return (NULL);
if (!use_lessopen)
return (NULL);
ch_ungetchar(-1);
if ((lessopen = lgetenv("LESSOPEN")) == NULL)
@ -875,7 +896,7 @@ public char * open_altfile(char *filename, int *pf, void **pfd)
}
qfilename = shell_quote(filename);
len = (int) (strlen(lessopen) + strlen(qfilename) + 2);
len = strlen(lessopen) + strlen(qfilename) + 2;
cmd = (char *) ecalloc(len, sizeof(char));
SNPRINTF1(cmd, len, lessopen, qfilename);
free(qfilename);
@ -891,7 +912,7 @@ public char * open_altfile(char *filename, int *pf, void **pfd)
#if HAVE_FILENO
if (returnfd)
{
char c;
unsigned char c;
int f;
/*
@ -945,19 +966,18 @@ public char * open_altfile(char *filename, int *pf, void **pfd)
/*
* Close a replacement file.
*/
public void close_altfile(char *altfilename, char *filename)
public void close_altfile(constant char *altfilename, constant char *filename)
{
#if HAVE_POPEN
char *lessclose;
constant char *lessclose;
char *qfilename;
char *qaltfilename;
FILE *fd;
char *cmd;
int len;
size_t len;
if (secure)
if (!secure_allow(SF_LESSOPEN))
return;
ch_ungetchar(-1);
if ((lessclose = lgetenv("LESSCLOSE")) == NULL)
return;
if (num_pct_s(lessclose) > 2)
@ -967,7 +987,7 @@ public void close_altfile(char *altfilename, char *filename)
}
qfilename = shell_quote(filename);
qaltfilename = shell_quote(altfilename);
len = (int) (strlen(lessclose) + strlen(qfilename) + strlen(qaltfilename) + 2);
len = strlen(lessclose) + strlen(qfilename) + strlen(qaltfilename) + 2;
cmd = (char *) ecalloc(len, sizeof(char));
SNPRINTF2(cmd, len, lessclose, qfilename, qaltfilename);
free(qaltfilename);
@ -982,16 +1002,16 @@ public void close_altfile(char *altfilename, char *filename)
/*
* Is the specified file a directory?
*/
public int is_dir(char *filename)
public lbool is_dir(constant char *filename)
{
int isdir = 0;
lbool isdir = FALSE;
#if HAVE_STAT
{
int r;
struct stat statbuf;
less_stat_t statbuf;
r = stat(filename, &statbuf);
r = less_stat(filename, &statbuf);
isdir = (r >= 0 && S_ISDIR(statbuf.st_mode));
}
#else
@ -1014,7 +1034,7 @@ public int is_dir(char *filename)
* is an ordinary file, otherwise an error message
* (if it cannot be opened or is a directory, etc.)
*/
public char * bad_file(char *filename)
public char * bad_file(constant char *filename)
{
char *m = NULL;
@ -1030,9 +1050,9 @@ public char * bad_file(char *filename)
{
#if HAVE_STAT
int r;
struct stat statbuf;
less_stat_t statbuf;
r = stat(filename, &statbuf);
r = less_stat(filename, &statbuf);
if (r < 0)
{
m = errno_message(filename);
@ -1059,9 +1079,9 @@ public char * bad_file(char *filename)
public POSITION filesize(int f)
{
#if HAVE_STAT
struct stat statbuf;
less_stat_t statbuf;
if (fstat(f, &statbuf) >= 0)
if (less_fstat(f, &statbuf) >= 0)
return ((POSITION) statbuf.st_size);
#else
#ifdef _OSK
@ -1074,7 +1094,7 @@ public POSITION filesize(int f)
return (seek_filesize(f));
}
public int curr_ifile_changed(void)
public lbool curr_ifile_changed(void)
{
#if HAVE_STAT_INO
/*
@ -1096,7 +1116,7 @@ public int curr_ifile_changed(void)
/*
*
*/
public char * shell_coption(void)
public constant char * shell_coption(void)
{
return ("-c");
}
@ -1104,9 +1124,9 @@ public char * shell_coption(void)
/*
* Return last component of a pathname.
*/
public char * last_component(char *name)
public constant char * last_component(constant char *name)
{
char *slash;
constant char *slash;
for (slash = name + strlen(name); slash > name; )
{

View file

@ -1,4 +1,4 @@
/* Generated by "./mkutable -f2 Cf -- unicode/UnicodeData.txt" on Mon Nov 14 18:19:23 PST 2022 */
/* Generated by "./mkutable -f2 Cf -- unicode/UnicodeData.txt" on Sun Sep 17 17:56:27 PDT 2023 */
{ 0x00ad, 0x00ad }, /* Cf */
{ 0x0600, 0x0605 }, /* Cf */
{ 0x061c, 0x061c }, /* Cf */

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -18,11 +18,11 @@
extern int less_is_more;
public int screen_trashed;
public int squished;
public lbool squished;
public int no_back_scroll = 0;
public int forw_prompt;
public int first_time = 1;
public lbool no_eof_bell = FALSE;
extern int sigs;
extern int top_scroll;
@ -30,17 +30,16 @@ extern int quiet;
extern int sc_width, sc_height;
extern int hshift;
extern int auto_wrap;
extern int plusoption;
extern lbool plusoption;
extern int forw_scroll;
extern int back_scroll;
extern int ignore_eoi;
extern int clear_bg;
extern int final_attr;
extern int header_lines;
extern int header_cols;
extern int full_screen;
extern POSITION header_start_pos;
#if HILITE_SEARCH
extern int size_linebuf;
extern size_t size_linebuf;
extern int hilite_search;
extern int status_col;
#endif
@ -53,6 +52,8 @@ extern char *tagoption;
*/
public void eof_bell(void)
{
if (no_eof_bell)
return;
#if HAVE_TIME
static time_type last_eof_bell = 0;
time_type now = get_time();
@ -69,19 +70,19 @@ public void eof_bell(void)
/*
* Check to see if the end of file is currently displayed.
*/
public int eof_displayed(void)
public lbool eof_displayed(void)
{
POSITION pos;
if (ignore_eoi)
return (0);
return (FALSE);
if (ch_length() == NULL_POSITION)
/*
* If the file length is not known,
* we can't possibly be displaying EOF.
*/
return (0);
return (FALSE);
/*
* If the bottom line is empty, we are at EOF.
@ -95,13 +96,13 @@ public int eof_displayed(void)
/*
* Check to see if the entire file is currently displayed.
*/
public int entire_file_displayed(void)
public lbool entire_file_displayed(void)
{
POSITION pos;
/* Make sure last line of file is displayed. */
if (!eof_displayed())
return (0);
return (FALSE);
/* Make sure first line of file is displayed. */
pos = position(0);
@ -118,7 +119,7 @@ public void squish_check(void)
{
if (!squished)
return;
squished = 0;
squished = FALSE;
repaint();
}
@ -144,13 +145,13 @@ static POSITION forw_line_pfx(POSITION pos, int pfx, int skipeol)
/*
* Set header text color.
* Underline last line of headers, but not at beginning of file
* Underline last line of headers, but not at header_start_pos
* (where there is no gap between the last header line and the next line).
*/
static void set_attr_header(int ln)
{
set_attr_line(AT_COLOR_HEADER);
if (ln+1 == header_lines && position(0) != ch_zero())
if (ln+1 == header_lines && position(0) != header_start_pos)
set_attr_line(AT_UNDERLINE);
}
@ -160,13 +161,13 @@ static void set_attr_header(int ln)
*/
public int overlay_header(void)
{
POSITION pos = ch_zero(); /* header lines are at beginning of file */
int ln;
int moved = FALSE;
lbool moved = FALSE;
if (header_lines > 0)
{
/* Draw header_lines lines from start of file at top of screen. */
POSITION pos = header_start_pos;
home();
for (ln = 0; ln < header_lines; ++ln)
{
@ -180,8 +181,8 @@ public int overlay_header(void)
if (header_cols > 0)
{
/* Draw header_cols columns at left of each line. */
POSITION pos = header_start_pos;
home();
pos = ch_zero();
for (ln = 0; ln < sc_height-1; ++ln)
{
if (ln >= header_lines) /* switch from header lines to normal lines */
@ -212,11 +213,13 @@ public int overlay_header(void)
* real line. If nblank > 0, the pos must be NULL_POSITION.
* The first real line after the blanks will start at ch_zero().
*/
public void forw(int n, POSITION pos, int force, int only_last, int nblank)
public void forw(int n, POSITION pos, lbool force, lbool only_last, int nblank)
{
int nlines = 0;
int do_repaint;
lbool do_repaint;
if (pos != NULL_POSITION)
pos = after_header_pos(pos);
squish_check();
/*
@ -233,7 +236,7 @@ public void forw(int n, POSITION pos, int force, int only_last, int nblank)
#if HILITE_SEARCH
if (pos != NULL_POSITION && (hilite_search == OPT_ONPLUS || is_filtering() || status_col)) {
prep_hilite(pos, pos + 4*size_linebuf, ignore_eoi ? 1 : -1);
prep_hilite(pos, pos + (POSITION) (4*size_linebuf), ignore_eoi ? 1 : -1);
pos = next_unfiltered(pos);
}
#endif
@ -250,7 +253,7 @@ public void forw(int n, POSITION pos, int force, int only_last, int nblank)
*/
pos_clear();
add_forw_pos(pos);
force = 1;
force = TRUE;
if (less_is_more == 0) {
clear();
home();
@ -266,7 +269,7 @@ public void forw(int n, POSITION pos, int force, int only_last, int nblank)
*/
pos_clear();
add_forw_pos(pos);
force = 1;
force = TRUE;
if (top_scroll)
{
clear();
@ -344,7 +347,7 @@ public void forw(int n, POSITION pos, int force, int only_last, int nblank)
#endif
!plusoption)
{
squished = 1;
squished = TRUE;
continue;
}
put_line();
@ -371,19 +374,6 @@ public void forw(int n, POSITION pos, int force, int only_last, int nblank)
#endif
forw_prompt = 1;
}
if (header_lines > 0)
{
/*
* Don't allow ch_zero to appear on screen except at top of screen.
* Otherwise duplicate header lines may be displayed.
*/
if (onscreen(ch_zero()) > 0)
{
jump_loc(ch_zero(), 0); /* {{ yuck }} */
return;
}
}
if (nlines == 0 && !ignore_eoi)
eof_bell();
else if (do_repaint)
@ -400,16 +390,16 @@ public void forw(int n, POSITION pos, int force, int only_last, int nblank)
/*
* Display n lines, scrolling backward.
*/
public void back(int n, POSITION pos, int force, int only_last)
public void back(int n, POSITION pos, lbool force, lbool only_last)
{
int nlines = 0;
int do_repaint;
lbool do_repaint;
squish_check();
do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1) || header_lines > 0);
#if HILITE_SEARCH
if (pos != NULL_POSITION && (hilite_search == OPT_ONPLUS || is_filtering() || status_col)) {
prep_hilite((pos < 3*size_linebuf) ? 0 : pos - 3*size_linebuf, pos, -1);
prep_hilite((pos < (POSITION) (3*size_linebuf)) ? 0 : pos - (POSITION) (3*size_linebuf), pos, -1);
}
#endif
while (--n >= 0)
@ -420,7 +410,6 @@ public void back(int n, POSITION pos, int force, int only_last)
#if HILITE_SEARCH
pos = prev_unfiltered(pos);
#endif
pos = back_line(pos);
if (pos == NULL_POSITION)
{
@ -430,6 +419,13 @@ public void back(int n, POSITION pos, int force, int only_last)
if (!force)
break;
}
if (pos != after_header_pos(pos))
{
/*
* Don't allow scrolling back to before the current header line.
*/
break;
}
/*
* Add the position of the previous line to the position table.
* Display the line on the screen.
@ -459,7 +455,7 @@ public void back(int n, POSITION pos, int force, int only_last)
* Display n more lines, forward.
* Start just after the line currently displayed at the bottom of the screen.
*/
public void forward(int n, int force, int only_last)
public void forward(int n, lbool force, lbool only_last)
{
POSITION pos;
@ -507,7 +503,7 @@ public void forward(int n, int force, int only_last)
* Display n more lines, backward.
* Start just before the line currently displayed at the top of the screen.
*/
public void backward(int n, int force, int only_last)
public void backward(int n, lbool force, lbool only_last)
{
POSITION pos;
@ -515,7 +511,7 @@ public void backward(int n, int force, int only_last)
if (pos == NULL_POSITION && (!force || position(BOTTOM) == 0))
{
eof_bell();
return;
return;
}
back(n, pos, force, only_last);
}

View file

@ -1,12 +1,16 @@
public char * saven(constant char *s, size_t n);
public char * save(constant char *s);
public void out_of_memory(void);
public void * ecalloc(int count, unsigned int size);
public void * ecalloc(size_t count, size_t size);
public char * skipsp(char *s);
public int sprefix(char *ps, char *s, int uppercase);
public constant char * skipspc(constant char *s);
public size_t sprefix(constant char *ps, constant char *s, int uppercase);
public void quit(int status);
public int secure_allow(int features);
public void raw_mode(int on);
public void scrsize(void);
public char * special_key_str(int key);
public void screen_size_changed(void);
public constant char * special_key_str(int key);
public void init_win_colors(void);
public void get_term(void);
public void init_mouse(void);
public void deinit_mouse(void);
@ -27,18 +31,19 @@ public void bell(void);
public void clear(void);
public void clear_eol(void);
public void clear_bot(void);
public COLOR_TYPE parse_color(char *str, int *p_fg, int *p_bg);
public COLOR_TYPE parse_color(constant char *str, mutable int *p_fg, mutable int *p_bg, mutable CHAR_ATTR *p_cattr);
public void at_enter(int attr);
public void at_exit(void);
public void at_switch(int attr);
public int is_at_equiv(int attr1, int attr2);
public lbool is_at_equiv(int attr1, int attr2);
public int apply_at_specials(int attr);
public void putbs(void);
public int win32_kbhit(void);
public char WIN32getch(void);
public void WIN32ungetch(int ch);
public lbool win32_kbhit(void);
public char WIN32getch(void);
public void win32_getch_clear(void);
public void WIN32setcolors(int fg, int bg);
public void WIN32textout(char *text, int len);
public void WIN32textout(constant char *text, size_t len);
public void match_brac(char obrac, char cbrac, int forwdir, int n);
public void ch_ungetchar(int c);
public void end_logfile(void);
@ -51,73 +56,84 @@ public POSITION ch_length(void);
public POSITION ch_tell(void);
public int ch_forw_get(void);
public int ch_back_get(void);
public void ch_setbufspace(int bufspace);
public void ch_setbufspace(ssize_t bufspace);
public void ch_flush(void);
public int seekable(int f);
public void ch_set_eof(void);
public void ch_init(int f, int flags);
public void ch_init(int f, int flags, ssize_t nread);
public void ch_close(void);
public int ch_getflags(void);
public void setfmt(char *s, char **fmtvarptr, int *attrptr, char *default_fmt, int for_printf);
public void setfmt(constant char *s, constant char **fmtvarptr, int *attrptr, constant char *default_fmt, lbool for_printf);
public void init_charset(void);
public int binary_char(LWCHAR c);
public int control_char(LWCHAR c);
public char * prchar(LWCHAR c);
public char * prutfchar(LWCHAR ch);
public int utf_len(int ch);
public int is_utf8_well_formed(char *ss, int slen);
public void utf_skip_to_lead(char **pp, char *limit);
public LWCHAR get_wchar(constant char *p);
public void put_wchar(char **pp, LWCHAR ch);
public lbool binary_char(LWCHAR c);
public lbool control_char(LWCHAR c);
public constant char * prchar(LWCHAR c);
public constant char * prutfchar(LWCHAR ch);
public int utf_len(char ch);
public lbool is_utf8_well_formed(constant char *ss, int slen);
public void utf_skip_to_lead(constant char **pp, constant char *limit);
public LWCHAR get_wchar(constant char *sp);
public void put_wchar(mutable char **pp, LWCHAR ch);
public LWCHAR step_charc(constant char **pp, signed int dir, constant char *limit);
public LWCHAR step_char(char **pp, signed int dir, constant char *limit);
public int is_composing_char(LWCHAR ch);
public int is_ubin_char(LWCHAR ch);
public int is_wide_char(LWCHAR ch);
public int is_combining_char(LWCHAR ch1, LWCHAR ch2);
public lbool is_composing_char(LWCHAR ch);
public lbool is_ubin_char(LWCHAR ch);
public lbool is_wide_char(LWCHAR ch);
public lbool is_combining_char(LWCHAR ch1, LWCHAR ch2);
public void cmd_reset(void);
public void clear_cmd(void);
public void cmd_putstr(constant char *s);
public int len_cmdbuf(void);
public void cmd_repaint(constant char *old_cp);
public void set_mlist(void *mlist, int cmdflags);
public void cmd_addhist(struct mlist *mlist, constant char *cmd, int modified);
public ssize_t save_updown_match(void);
public void restore_updown_match(ssize_t udm);
public void cmd_addhist(struct mlist *mlist, constant char *cmd, lbool modified);
public void cmd_accept(void);
public int cmd_char(int c);
public LINENUM cmd_int(long *frac);
public char * get_cmdbuf(void);
public char * cmd_lastpattern(void);
public int cmd_char(char c);
public LINENUM cmd_int(mutable long *frac);
public constant char * get_cmdbuf(void);
public constant char * cmd_lastpattern(void);
public void init_cmdhist(void);
public void save_cmdhist(void);
public int in_mca(void);
public int norm_search_type(int st);
public void screen_trashed_num(int trashed);
public void screen_trashed(void);
public int is_screen_trashed(void);
public void dispversion(void);
public int getcc(void);
public void ungetcc(LWCHAR c);
public void ungetcc_back(LWCHAR c);
public void ungetsc(char *s);
public LWCHAR peekcc(void);
public void getcc_clear(void);
public char getcc(void);
public void ungetcc(char c);
public void ungetcc_back(char c);
public void ungetcc_end_command(void);
public void ungetsc(constant char *s);
public char peekcc(void);
public void commands(void);
public int cvt_length(int len, int ops);
public int * cvt_alloc_chpos(int len);
public void cvt_text(char *odst, char *osrc, int *chpos, int *lenp, int ops);
public size_t cvt_length(size_t len, int ops);
public int * cvt_alloc_chpos(size_t len);
public void cvt_text(mutable char *odst, constant char *osrc, mutable int *chpos, mutable size_t *lenp, int ops);
public void expand_cmd_tables(void);
public void init_cmds(void);
public void add_fcmd_table(char *buf, int len);
public void add_ecmd_table(char *buf, int len);
public int fcmd_decode(char *cmd, char **sp);
public int ecmd_decode(char *cmd, char **sp);
public char * lgetenv(char *var);
public int isnullenv(char *s);
public int lesskey(char *filename, int sysvar);
public int lesskey_src(char *filename, int sysvar);
public int add_hometable(int (*call_lesskey)(char *, int), char *envname, char *def_filename, int sysvar);
public int editchar(int c, int flags);
public void init_textlist(struct textlist *tlist, char *str);
public char * forw_textlist(struct textlist *tlist, char *prev);
public char * back_textlist(struct textlist *tlist, char *prev);
public void add_fcmd_table(unsigned char *buf, size_t len);
public void add_ecmd_table(unsigned char *buf, size_t len);
public void add_uvar_table(unsigned char *buf, size_t len);
public void add_sysvar_table(unsigned char *buf, size_t len);
public int fcmd_decode(constant char *cmd, constant char **sp);
public int ecmd_decode(constant char *cmd, constant char **sp);
public constant char * lgetenv(constant char *var);
public constant char * lgetenv_ext(constant char *var, unsigned char *env_buf, size_t env_buf_len);
public lbool isnullenv(constant char *s);
public int lesskey(constant char *filename, lbool sysvar);
public int lesskey_src(constant char *filename, lbool sysvar);
public int lesskey_content(constant char *content, lbool sysvar);
public int editchar(char c, int flags);
public void init_textlist(struct textlist *tlist, mutable char *str);
public constant char * forw_textlist(struct textlist *tlist, constant char *prev);
public constant char * back_textlist(struct textlist *tlist, constant char *prev);
public void close_altpipe(IFILE ifile);
public void check_altpipe_error(void);
public int edit(char *filename);
public int edit(constant char *filename);
public int edit_ifile(IFILE ifile);
public int edit_list(char *filelist);
public int edit_first(void);
@ -131,35 +147,38 @@ public void reedit_ifile(IFILE save_ifile);
public void reopen_curr_ifile(void);
public int edit_stdin(void);
public void cat_file(void);
public void use_logfile(char *filename);
public char * shell_unquote(char *str);
public char * get_meta_escape(void);
public char * shell_quote(char *s);
public char * dirfile(char *dirname, char *filename, int must_exist);
public char * homefile(char *filename);
public char * fexpand(char *s);
public char * fcomplete(char *s);
public int bin_file(int f);
public char * lglob(char *filename);
public int is_fake_pathname(char *path);
public char * lrealpath(char *path);
public char * open_altfile(char *filename, int *pf, void **pfd);
public void close_altfile(char *altfilename, char *filename);
public int is_dir(char *filename);
public char * bad_file(char *filename);
public void use_logfile(constant char *filename);
public void expand_evars(mutable char *buf, size_t len, struct xbuffer *xbuf);
public char * shell_unquote(constant char *str);
public constant char * get_meta_escape(void);
public char * shell_quoten(constant char *s, size_t slen);
public char * shell_quote(constant char *s);
public char * dirfile(constant char *dirname, constant char *filename, int must_exist);
public char * homefile(constant char *filename);
public char * fexpand(constant char *s);
public char * fcomplete(constant char *s);
public int bin_file(int f, ssize_t *n);
public char * readfd(FILE *fd);
public char * lglob(constant char *afilename);
public lbool is_fake_pathname(constant char *path);
public char * lrealpath(constant char *path);
public char * open_altfile(constant char *filename, int *pf, void **pfd);
public void close_altfile(constant char *altfilename, constant char *filename);
public lbool is_dir(constant char *filename);
public char * bad_file(constant char *filename);
public POSITION filesize(int f);
public int curr_ifile_changed(void);
public char * shell_coption(void);
public char * last_component(char *name);
public lbool curr_ifile_changed(void);
public constant char * shell_coption(void);
public constant char * last_component(constant char *name);
public void eof_bell(void);
public int eof_displayed(void);
public int entire_file_displayed(void);
public lbool eof_displayed(void);
public lbool entire_file_displayed(void);
public void squish_check(void);
public int overlay_header(void);
public void forw(int n, POSITION pos, int force, int only_last, int nblank);
public void back(int n, POSITION pos, int force, int only_last);
public void forward(int n, int force, int only_last);
public void backward(int n, int force, int only_last);
public void forw(int n, POSITION pos, lbool force, lbool only_last, int nblank);
public void back(int n, POSITION pos, lbool force, lbool only_last);
public void forward(int n, lbool force, lbool only_last);
public void backward(int n, lbool force, lbool only_last);
public int get_back_scroll(void);
public int get_one_screen(void);
public void del_ifile(IFILE h);
@ -167,9 +186,9 @@ public IFILE next_ifile(IFILE h);
public IFILE prev_ifile(IFILE h);
public IFILE getoff_ifile(IFILE ifile);
public int nifile(void);
public IFILE get_ifile(char *filename, IFILE prev);
public char * get_filename(IFILE ifile);
public char * get_real_filename(IFILE ifile);
public IFILE get_ifile(constant char *filename, IFILE prev);
public constant char * get_filename(IFILE ifile);
public constant char * get_real_filename(IFILE ifile);
public int get_index(IFILE ifile);
public void store_pos(IFILE ifile, struct scrpos *scrpos);
public void get_pos(IFILE ifile, struct scrpos *scrpos);
@ -184,7 +203,7 @@ public void *get_altpipe(IFILE ifile);
public void set_altfilename(IFILE ifile, char *altfilename);
public char * get_altfilename(IFILE ifile);
public void if_dump(void);
public POSITION forw_line_seg(POSITION curr_pos, int skipeol, int rscroll, int nochop);
public POSITION forw_line_seg(POSITION curr_pos, lbool skipeol, lbool rscroll, lbool nochop);
public POSITION forw_line(POSITION curr_pos);
public POSITION back_line(POSITION curr_pos);
public void set_attnpos(POSITION pos);
@ -194,9 +213,10 @@ public void jump_back(LINENUM linenum);
public void repaint(void);
public void jump_percent(int percent, long fraction);
public void jump_line_loc(POSITION pos, int sline);
public POSITION after_header_pos(POSITION pos);
public void jump_loc(POSITION pos, int sline);
public void init_line(void);
public int is_ascii_char(LWCHAR ch);
public lbool is_ascii_char(LWCHAR ch);
public POSITION line_position(void);
public void prewind(void);
public void plinestart(POSITION pos);
@ -205,26 +225,31 @@ public void pshift_all(void);
public int pwidth(LWCHAR ch, int a, LWCHAR prev_ch, int prev_a);
public void savec(void);
public void loadc(void);
public int is_ansi_end(LWCHAR ch);
public int is_ansi_middle(LWCHAR ch);
public void skip_ansi(struct ansi_state *pansi, char **pp, constant char *limit);
public lbool is_ansi_end(LWCHAR ch);
public lbool is_ansi_middle(LWCHAR ch);
public void skip_ansi(struct ansi_state *pansi, constant char **pp, constant char *limit);
public struct ansi_state * ansi_start(LWCHAR ch);
public int ansi_step(struct ansi_state *pansi, LWCHAR ch);
public ansi_state ansi_step(struct ansi_state *pansi, LWCHAR ch);
public osc8_state ansi_osc8_state(struct ansi_state *pansi);
public void ansi_done(struct ansi_state *pansi);
public int pappend(int c, POSITION pos);
public int pappend_b(char c, POSITION pos, lbool before_pendc);
public int pappend(char c, POSITION pos);
public int pflushmbc(void);
public void pdone(int endline, int chopped, int forw);
public int col_from_pos(POSITION linepos, POSITION spos, POSITION saved_pos, int saved_col);
public POSITION pos_from_col(POSITION linepos, int col, POSITION saved_pos, int saved_col);
public void set_attr_line(int a);
public void set_status_col(char c, int attr);
public int gline(int i, int *ap);
public int gline(size_t i, int *ap);
public void null_line(void);
public POSITION forw_raw_line(POSITION curr_pos, char **linep, int *line_lenp);
public POSITION back_raw_line(POSITION curr_pos, char **linep, int *line_lenp);
public int skip_columns(int cols, char **linep, int *line_lenp);
public POSITION forw_raw_line_len(POSITION curr_pos, size_t read_len, constant char **linep, size_t *line_lenp);
public POSITION forw_raw_line(POSITION curr_pos, constant char **linep, size_t *line_lenp);
public POSITION back_raw_line(POSITION curr_pos, constant char **linep, size_t *line_lenp);
public int skip_columns(int cols, constant char **linep, size_t *line_lenp);
public void load_line(constant char *str);
public int rrshift(void);
public int set_color_map(int attr, char *colorstr);
public char * get_color_map(int attr);
public int set_color_map(int attr, constant char *colorstr);
public constant char * get_color_map(int attr);
public void clr_linenum(void);
public void add_lnum(LINENUM linenum, POSITION pos);
public LINENUM find_linenum(POSITION pos);
@ -232,75 +257,85 @@ public POSITION find_pos(LINENUM linenum);
public LINENUM currline(int where);
public void scan_eof(void);
public LINENUM vlinenum(LINENUM linenum);
public void lsystem(char *cmd, char *donemsg);
public int pipe_mark(int c, char *cmd);
public int pipe_data(char *cmd, POSITION spos, POSITION epos);
public void lsystem(constant char *cmd, constant char *donemsg);
public int pipe_mark(char c, constant char *cmd);
public int pipe_data(constant char *cmd, POSITION spos, POSITION epos);
public void init_mark(void);
public int badmark(LWCHAR c);
public void setmark(LWCHAR c, int where);
public void clrmark(LWCHAR c);
public int badmark(char c);
public void setmark(char c, int where);
public void clrmark(char c);
public void lastmark(void);
public void gomark(LWCHAR c);
public POSITION markpos(LWCHAR c);
public void gomark(char c);
public POSITION markpos(char c);
public char posmark(POSITION pos);
public void unmark(IFILE ifile);
public void mark_check_ifile(IFILE ifile);
public void save_marks(FILE *fout, char *hdr);
public void restore_mark(char *line);
public void opt_o(int type, char *s);
public void opt__O(int type, char *s);
public void opt_j(int type, char *s);
public void save_marks(FILE *fout, constant char *hdr);
public void restore_mark(constant char *line);
public void opt_o(int type, constant char *s);
public void opt__O(int type, constant char *s);
public void opt_j(int type, constant char *s);
public void calc_jump_sline(void);
public void opt_shift(int type, char *s);
public void opt_shift(int type, constant char *s);
public void calc_shift_count(void);
public void opt_k(int type, char *s);
public void opt_ks(int type, char *s);
public void opt_t(int type, char *s);
public void opt__T(int type, char *s);
public void opt_p(int type, char *s);
public void opt__P(int type, char *s);
public void opt_b(int type, char *s);
public void opt_i(int type, char *s);
public void opt__V(int type, char *s);
public void opt_D(int type, char *s);
public void set_tabs(char *s, int len);
public void opt_x(int type, char *s);
public void opt_quote(int type, char *s);
public void opt_rscroll(int type, char *s);
public void opt_query(int type, char *s);
public void opt_mousecap(int type, char *s);
public void opt_wheel_lines(int type, char *s);
public void opt_linenum_width(int type, char *s);
public void opt_status_col_width(int type, char *s);
public void opt_filesize(int type, char *s);
public void opt_intr(int type, char *s);
public void opt_header(int type, char *s);
public void opt_search_type(int type, char *s);
public void opt_ttyin_name(int type, char *s);
public void opt_k(int type, constant char *s);
public void opt_ks(int type, constant char *s);
public void opt_kc(int type, constant char *s);
public void opt__S(int type, constant char *s);
public void opt_t(int type, constant char *s);
public void opt__T(int type, constant char *s);
public void opt_p(int type, constant char *s);
public void opt__P(int type, constant char *s);
public void opt_b(int type, constant char *s);
public void opt_i(int type, constant char *s);
public void opt__V(int type, constant char *s);
public void opt_D(int type, constant char *s);
public void set_tabs(constant char *s, size_t len);
public void opt_x(int type, constant char *s);
public void opt_quote(int type, constant char *s);
public void opt_rscroll(int type, constant char *s);
public void opt_query(int type, constant char *s);
public void opt_match_shift(int type, constant char *s);
public void calc_match_shift(void);
public void opt_mousecap(int type, constant char *s);
public void opt_wheel_lines(int type, constant char *s);
public void opt_linenum_width(int type, constant char *s);
public void opt_status_col_width(int type, constant char *s);
public void opt_filesize(int type, constant char *s);
public void opt_intr(int type, constant char *s);
public int next_cnum(constant char **sp, constant char *printopt, constant char *errmsg, lbool *errp);
public void opt_header(int type, constant char *s);
public void opt_search_type(int type, constant char *s);
public void opt_nosearch_headers(int type, constant char *s);
public void opt_nosearch_header_lines(int type, constant char *s);
public void opt_nosearch_header_cols(int type, constant char *s);
public void opt_ttyin_name(int type, constant char *s);
public int chop_line(void);
public int get_swindow(void);
public char * propt(int c);
public void scan_option(char *s);
public void toggle_option(struct loption *o, int lower, char *s, int how_toggle);
public constant char * propt(char c);
public void scan_option(constant char *s);
public void toggle_option(struct loption *o, int lower, constant char *s, int how_toggle);
public int opt_has_param(struct loption *o);
public char * opt_prompt(struct loption *o);
public char * opt_toggle_disallowed(int c);
public int isoptpending(void);
public constant char * opt_prompt(struct loption *o);
public constant char * opt_toggle_disallowed(int c);
public lbool isoptpending(void);
public void nopendopt(void);
public int getnum(char **sp, char *printopt, int *errp);
public long getfraction(char **sp, char *printopt, int *errp);
public int getnumc(constant char **sp, constant char *printopt, lbool *errp);
public int getnum(char **sp, constant char *printopt, lbool *errp);
public long getfraction(constant char **sp, constant char *printopt, lbool *errp);
public void init_unsupport(void);
public int get_quit_at_eof(void);
public void init_option(void);
public struct loption * findopt(int c);
public struct loption * findopt_name(char **p_optname, char **p_oname, int *p_err);
public struct loption * findopt_name(constant char **p_optname, constant char **p_oname, lbool *p_ambig);
public void init_poll(void);
public int supports_ctrl_x(void);
public int iread(int fd, unsigned char *buf, unsigned int len);
public ssize_t iread(int fd, unsigned char *buf, size_t len);
public void intread(void);
public time_type get_time(void);
public char * errno_message(char *filename);
public char * signal_message(int sig);
public uintmax muldiv(uintmax val, uintmax num, uintmax den);
public char * errno_message(constant char *filename);
public constant char * signal_message(int sig);
public uintmax umuldiv(uintmax val, uintmax num, uintmax den);
public int percentage(POSITION num, POSITION den);
public POSITION percent_pos(POSITION pos, int percent, long fraction);
public int os9_signal(int type, RETSIGTYPE (*handler)());
@ -308,21 +343,21 @@ public void sleep_ms(int ms);
public void put_line(void);
public void flush(void);
public void set_output(int fd);
public int putchr(int c);
public int putchr(int ch);
public void clear_bot_if_needed(void);
public void putstr(constant char *s);
public int less_printf(char *fmt, PARG *parg);
public int less_printf(constant char *fmt, PARG *parg);
public void get_return(void);
public void error(char *fmt, PARG *parg);
public void ierror(char *fmt, PARG *parg);
public void ixerror(char *fmt, PARG *parg);
public int query(char *fmt, PARG *parg);
public int compile_pattern(char *pattern, int search_type, int show_error, PATTERN_TYPE *comp_pattern);
public void error(constant char *fmt, PARG *parg);
public void ierror(constant char *fmt, PARG *parg);
public void ixerror(constant char *fmt, PARG *parg);
public int query(constant char *fmt, PARG *parg);
public int compile_pattern(constant char *pattern, int search_type, int show_error, PATTERN_TYPE *comp_pattern);
public void uncompile_pattern(PATTERN_TYPE *pattern);
public int valid_pattern(char *pattern);
public int is_null_pattern(PATTERN_TYPE pattern);
public int match_pattern(PATTERN_TYPE pattern, char *tpattern, char *line, int line_len, char **sp, char **ep, int nsp, int notbol, int search_type);
public char * pattern_lib_name(void);
public lbool is_null_pattern(PATTERN_TYPE pattern);
public int match_pattern(PATTERN_TYPE pattern, constant char *tpattern, constant char *line, size_t line_len, constant char **sp, constant char **ep, int nsp, int notbol, int search_type);
public constant char * pattern_lib_name(void);
public POSITION position(int sindex);
public void add_forw_pos(POSITION pos);
public void add_back_pos(POSITION pos);
@ -333,40 +368,48 @@ public int empty_screen(void);
public int empty_lines(int s, int e);
public void get_scrpos(struct scrpos *scrpos, int where);
public int sindex_from_sline(int sline);
public void pos_rehead(void);
public void init_prompt(void);
public char * pr_expand(constant char *proto);
public char * eq_message(void);
public char * pr_string(void);
public char * wait_message(void);
public constant char * pr_expand(constant char *proto);
public constant char * eq_message(void);
public constant char * pr_string(void);
public constant char * wait_message(void);
public void init_search(void);
public void repaint_hilite(int on);
public int get_cvt_ops(int search_type);
public void repaint_hilite(lbool on);
public void clear_attn(void);
public void undo_search(int clear);
public void undo_search(lbool clear);
public void clr_hlist(struct hilite_tree *anchor);
public void clr_hilite(void);
public void clr_filter(void);
public int is_filtered(POSITION pos);
public void set_header(POSITION pos);
public lbool is_filtered(POSITION pos);
public POSITION next_unfiltered(POSITION pos);
public POSITION prev_unfiltered(POSITION pos);
public int is_hilited_attr(POSITION pos, POSITION epos, int nohide, int *p_matches);
public void chg_hilite(void);
public void osc8_search(int search_type, constant char *param, int matches);
public lbool osc8_click(int sindex, int col);
public void osc8_open(void);
public void osc8_jump(void);
public void chg_caseless(void);
public int search(int search_type, char *pattern, int n);
public int search(int search_type, constant char *pattern, int n);
public void prep_hilite(POSITION spos, POSITION epos, int maxlines);
public void set_filter_pattern(char *pattern, int search_type);
public int is_filtering(void);
public void set_filter_pattern(constant char *pattern, int search_type);
public lbool is_filtering(void);
public RETSIGTYPE winch(int type);
public void init_signals(int on);
public void psignals(void);
public void cleantags(void);
public int gettagtype(void);
public void findtag(char *tag);
public void findtag(constant char *tag);
public POSITION tagsearch(void);
public char * nexttag(int n);
public char * prevtag(int n);
public constant char * nexttag(int n);
public constant char * prevtag(int n);
public int ntags(void);
public int curr_tag(void);
public int edit_tagfile(void);
public lbool is_lesstest(void);
public int open_tty(void);
public void open_getchr(void);
public void close_getchr(void);
@ -374,12 +417,14 @@ public int pclose(FILE *f);
public int default_wheel_lines(void);
public int getchr(void);
public void xbuf_init(struct xbuffer *xbuf);
public void xbuf_init_size(struct xbuffer *xbuf, size_t init_size);
public void xbuf_deinit(struct xbuffer *xbuf);
public void xbuf_reset(struct xbuffer *xbuf);
public void xbuf_add_byte(struct xbuffer *xbuf, unsigned char b);
public void xbuf_add_data(struct xbuffer *xbuf, unsigned char *data, int len);
public void xbuf_add_char(struct xbuffer *xbuf, char c);
public void xbuf_add_data(struct xbuffer *xbuf, constant unsigned char *data, size_t len);
public int xbuf_pop(struct xbuffer *buf);
public void xbuf_set(struct xbuffer *dst, struct xbuffer *src);
public char * xbuf_char_data(struct xbuffer *xbuf);
public int help_ckd_add(void *r, uintmax a, uintmax b, int rsize, int rsigned);
public int help_ckd_mul(void *r, uintmax a, uintmax b, int rsize, int rsigned);
public constant char * xbuf_char_data(constant struct xbuffer *xbuf);
public lbool help_ckd_add(void *r, uintmax a, uintmax b, int rsize, int rsigned);
public lbool help_ckd_mul(void *r, uintmax a, uintmax b, int rsize, int rsigned);

View file

@ -1,4 +1,4 @@
/* This file was generated by mkhelp.pl from less.hlp at 22:43 on 2023/7/20 */
/* This file was generated by mkhelp.pl from less.hlp at 22:29 on 2024/10/6 */
#include "less.h"
constant char helpdata[] = {
'\n',
@ -44,6 +44,9 @@ constant char helpdata[] = {
' ',' ','N',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','R','e','p','e','a','t',' ','p','r','e','v','i','o','u','s',' ','s','e','a','r','c','h',' ','i','n',' ','r','e','v','e','r','s','e',' ','d','i','r','e','c','t','i','o','n','.','\n',
' ',' ','E','S','C','-','n',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','R','e','p','e','a','t',' ','p','r','e','v','i','o','u','s',' ','s','e','a','r','c','h',',',' ','s','p','a','n','n','i','n','g',' ','f','i','l','e','s','.','\n',
' ',' ','E','S','C','-','N',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','R','e','p','e','a','t',' ','p','r','e','v','i','o','u','s',' ','s','e','a','r','c','h',',',' ','r','e','v','e','r','s','e',' ','d','i','r','.',' ','&',' ','s','p','a','n','n','i','n','g',' ','f','i','l','e','s','.','\n',
' ',' ','^','O','^','N',' ',' ','^','O','n',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','S','e','a','r','c','h',' ','f','o','r','w','a','r','d',' ','f','o','r',' ','(','_','\b','N','-','t','h',')',' ','O','S','C','8',' ','h','y','p','e','r','l','i','n','k','.','\n',
' ',' ','^','O','^','P',' ',' ','^','O','p',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','S','e','a','r','c','h',' ','b','a','c','k','w','a','r','d',' ','f','o','r',' ','(','_','\b','N','-','t','h',')',' ','O','S','C','8',' ','h','y','p','e','r','l','i','n','k','.','\n',
' ',' ','^','O','^','L',' ',' ','^','O','l',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','J','u','m','p',' ','t','o',' ','t','h','e',' ','c','u','r','r','e','n','t','l','y',' ','s','e','l','e','c','t','e','d',' ','O','S','C','8',' ','h','y','p','e','r','l','i','n','k','.','\n',
' ',' ','E','S','C','-','u',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','U','n','d','o',' ','(','t','o','g','g','l','e',')',' ','s','e','a','r','c','h',' ','h','i','g','h','l','i','g','h','t','i','n','g','.','\n',
' ',' ','E','S','C','-','U',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','C','l','e','a','r',' ','s','e','a','r','c','h',' ','h','i','g','h','l','i','g','h','t','i','n','g','.','\n',
' ',' ','&','_','\b','p','_','\b','a','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','n',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','D','i','s','p','l','a','y',' ','o','n','l','y',' ','m','a','t','c','h','i','n','g',' ','l','i','n','e','s','.','\n',
@ -56,6 +59,7 @@ constant char helpdata[] = {
' ',' ',' ',' ',' ',' ',' ',' ','^','R',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','u','s','e',' ','R','E','G','U','L','A','R',' ','E','X','P','R','E','S','S','I','O','N','S','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ','^','S',' ','_','\b','n',' ',' ',' ',' ',' ','S','e','a','r','c','h',' ','f','o','r',' ','m','a','t','c','h',' ','i','n',' ','_','\b','n','-','t','h',' ','p','a','r','e','n','t','h','e','s','i','z','e','d',' ','s','u','b','p','a','t','t','e','r','n','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ','^','W',' ',' ',' ',' ',' ',' ',' ','W','R','A','P',' ','s','e','a','r','c','h',' ','i','f',' ','n','o',' ','m','a','t','c','h',' ','f','o','u','n','d','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ','^','L',' ',' ',' ',' ',' ',' ',' ','E','n','t','e','r',' ','n','e','x','t',' ','c','h','a','r','a','c','t','e','r',' ','l','i','t','e','r','a','l','l','y',' ','i','n','t','o',' ','p','a','t','t','e','r','n','.','\n',
' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n',
'\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','J','\b','J','U','\b','U','M','\b','M','P','\b','P','I','\b','I','N','\b','N','G','\b','G','\n',
@ -95,6 +99,7 @@ constant char helpdata[] = {
' ',' ',':','n',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','E','x','a','m','i','n','e',' ','t','h','e',' ','(','_','\b','N','-','t','h',')',' ','n','e','x','t',' ','f','i','l','e',' ','f','r','o','m',' ','t','h','e',' ','c','o','m','m','a','n','d',' ','l','i','n','e','.','\n',
' ',' ',':','p',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','E','x','a','m','i','n','e',' ','t','h','e',' ','(','_','\b','N','-','t','h',')',' ','p','r','e','v','i','o','u','s',' ','f','i','l','e',' ','f','r','o','m',' ','t','h','e',' ','c','o','m','m','a','n','d',' ','l','i','n','e','.','\n',
' ',' ',':','x',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','E','x','a','m','i','n','e',' ','t','h','e',' ','f','i','r','s','t',' ','(','o','r',' ','_','\b','N','-','t','h',')',' ','f','i','l','e',' ','f','r','o','m',' ','t','h','e',' ','c','o','m','m','a','n','d',' ','l','i','n','e','.','\n',
' ',' ','^','O','^','O',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','O','p','e','n',' ','t','h','e',' ','c','u','r','r','e','n','t','l','y',' ','s','e','l','e','c','t','e','d',' ','O','S','C','8',' ','h','y','p','e','r','l','i','n','k','.','\n',
' ',' ',':','d',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','e','l','e','t','e',' ','t','h','e',' ','c','u','r','r','e','n','t',' ','f','i','l','e',' ','f','r','o','m',' ','t','h','e',' ','c','o','m','m','a','n','d',' ','l','i','n','e',' ','l','i','s','t','.','\n',
' ',' ','=',' ',' ','^','G',' ',' ',':','f',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','P','r','i','n','t',' ','c','u','r','r','e','n','t',' ','f','i','l','e',' ','n','a','m','e','.','\n',
' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n',
@ -158,8 +163,8 @@ constant char helpdata[] = {
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','c','r','e','e','n',' ','p','o','s','i','t','i','o','n',' ','o','f',' ','t','a','r','g','e','t',' ','l','i','n','e','s','.','\n',
' ',' ','-','J',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','s','t','a','t','u','s','-','c','o','l','u','m','n','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','i','s','p','l','a','y',' ','a',' ','s','t','a','t','u','s',' ','c','o','l','u','m','n',' ','a','t',' ','l','e','f','t',' ','e','d','g','e',' ','o','f',' ','s','c','r','e','e','n','.','\n',
' ',' ','-','k',' ','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']',' ',' ','.',' ',' ','-','-','l','e','s','s','k','e','y','-','f','i','l','e','=','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','U','s','e',' ','a',' ','l','e','s','s','k','e','y',' ','f','i','l','e','.','\n',
' ',' ','-','k',' ','_','\b','f','_','\b','i','_','\b','l','_','\b','e',' ',' ','.','.','.',' ',' ','-','-','l','e','s','s','k','e','y','-','f','i','l','e','=','_','\b','f','_','\b','i','_','\b','l','_','\b','e','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','U','s','e',' ','a',' ','c','o','m','p','i','l','e','d',' ','l','e','s','s','k','e','y',' ','f','i','l','e','.','\n',
' ',' ','-','K',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','q','u','i','t','-','o','n','-','i','n','t','r','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','x','i','t',' ','l','e','s','s',' ','i','n',' ','r','e','s','p','o','n','s','e',' ','t','o',' ','c','t','r','l','-','C','.','\n',
' ',' ','-','L',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','n','o','-','l','e','s','s','o','p','e','n','\n',
@ -170,11 +175,11 @@ constant char helpdata[] = {
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','u','p','p','r','e','s','s',' ','l','i','n','e',' ','n','u','m','b','e','r','s',' ','i','n',' ','p','r','o','m','p','t','s',' ','a','n','d',' ','m','e','s','s','a','g','e','s','.','\n',
' ',' ','-','N',' ','.','.','.','.','.','.','.','.','.',' ',' ','-','-','L','I','N','E','-','N','U','M','B','E','R','S','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','i','s','p','l','a','y',' ','l','i','n','e',' ','n','u','m','b','e','r',' ','a','t',' ','s','t','a','r','t',' ','o','f',' ','e','a','c','h',' ','l','i','n','e','.','\n',
' ',' ','-','o',' ','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']',' ',' ','.',' ',' ','-','-','l','o','g','-','f','i','l','e','=','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']','\n',
' ',' ','-','o',' ','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']',' ','.','.',' ',' ','-','-','l','o','g','-','f','i','l','e','=','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','C','o','p','y',' ','t','o',' ','l','o','g',' ','f','i','l','e',' ','(','s','t','a','n','d','a','r','d',' ','i','n','p','u','t',' ','o','n','l','y',')','.','\n',
' ',' ','-','O',' ','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']',' ',' ','.',' ',' ','-','-','L','O','G','-','F','I','L','E','=','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']','\n',
' ',' ','-','O',' ','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']',' ','.','.',' ',' ','-','-','L','O','G','-','F','I','L','E','=','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','C','o','p','y',' ','t','o',' ','l','o','g',' ','f','i','l','e',' ','(','u','n','c','o','n','d','i','t','i','o','n','a','l','l','y',' ','o','v','e','r','w','r','i','t','e',')','.','\n',
' ',' ','-','p',' ','[','_','\b','p','_','\b','a','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','n',']',' ',' ','-','-','p','a','t','t','e','r','n','=','[','_','\b','p','_','\b','a','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','n',']','\n',
' ',' ','-','p',' ','_','\b','p','_','\b','a','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','n',' ','.',' ',' ','-','-','p','a','t','t','e','r','n','=','[','_','\b','p','_','\b','a','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','n',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','t','a','r','t',' ','a','t',' ','p','a','t','t','e','r','n',' ','(','f','r','o','m',' ','c','o','m','m','a','n','d',' ','l','i','n','e',')','.','\n',
' ',' ','-','P',' ','[','_','\b','p','_','\b','r','_','\b','o','_','\b','m','_','\b','p','_','\b','t',']',' ',' ',' ','-','-','p','r','o','m','p','t','=','[','_','\b','p','_','\b','r','_','\b','o','_','\b','m','_','\b','p','_','\b','t',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','e','f','i','n','e',' ','n','e','w',' ','p','r','o','m','p','t','.','\n',
@ -186,7 +191,7 @@ constant char helpdata[] = {
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','q','u','e','e','z','e',' ','m','u','l','t','i','p','l','e',' ','b','l','a','n','k',' ','l','i','n','e','s','.','\n',
' ',' ','-','S',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','c','h','o','p','-','l','o','n','g','-','l','i','n','e','s','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','C','h','o','p',' ','(','t','r','u','n','c','a','t','e',')',' ','l','o','n','g',' ','l','i','n','e','s',' ','r','a','t','h','e','r',' ','t','h','a','n',' ','w','r','a','p','p','i','n','g','.','\n',
' ',' ','-','t',' ','[','_','\b','t','_','\b','a','_','\b','g',']',' ',' ','.','.',' ',' ','-','-','t','a','g','=','[','_','\b','t','_','\b','a','_','\b','g',']','\n',
' ',' ','-','t',' ','_','\b','t','_','\b','a','_','\b','g',' ',' ','.','.','.','.',' ',' ','-','-','t','a','g','=','[','_','\b','t','_','\b','a','_','\b','g',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','F','i','n','d',' ','a',' ','t','a','g','.','\n',
' ',' ','-','T',' ','[','_','\b','t','_','\b','a','_','\b','g','_','\b','s','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']',' ','-','-','t','a','g','-','f','i','l','e','=','[','_','\b','t','_','\b','a','_','\b','g','_','\b','s','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','U','s','e',' ','a','n',' ','a','l','t','e','r','n','a','t','e',' ','t','a','g','s',' ','f','i','l','e','.','\n',
@ -212,21 +217,28 @@ constant char helpdata[] = {
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','d','i','s','p','l','a','y',' ','t','i','l','d','e','s',' ','a','f','t','e','r',' ','e','n','d',' ','o','f',' ','f','i','l','e','.','\n',
' ',' ','-','#',' ','[','_','\b','N',']',' ',' ','.','.','.','.',' ',' ','-','-','s','h','i','f','t','=','[','_','\b','N',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','t',' ','h','o','r','i','z','o','n','t','a','l',' ','s','c','r','o','l','l',' ','a','m','o','u','n','t',' ','(','0',' ','=',' ','o','n','e',' ','h','a','l','f',' ','s','c','r','e','e','n',' ','w','i','d','t','h',')','.','\n',
'\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','e','x','i','t','-','f','o','l','l','o','w','-','o','n','-','c','l','o','s','e','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','x','i','t',' ','F',' ','c','o','m','m','a','n','d',' ','o','n',' ','a',' ','p','i','p','e',' ','w','h','e','n',' ','w','r','i','t','e','r',' ','c','l','o','s','e','s',' ','p','i','p','e','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','f','i','l','e','-','s','i','z','e','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','A','u','t','o','m','a','t','i','c','a','l','l','y',' ','d','e','t','e','r','m','i','n','e',' ','t','h','e',' ','s','i','z','e',' ','o','f',' ','t','h','e',' ','i','n','p','u','t',' ','f','i','l','e','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','f','o','l','l','o','w','-','n','a','m','e','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','T','h','e',' ','F',' ','c','o','m','m','a','n','d',' ','c','h','a','n','g','e','s',' ','f','i','l','e','s',' ','i','f',' ','t','h','e',' ','i','n','p','u','t',' ','f','i','l','e',' ','i','s',' ','r','e','n','a','m','e','d','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','h','e','a','d','e','r','=','[','_','\b','N','[',',','_','\b','M',']',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','U','s','e',' ','N',' ','l','i','n','e','s',' ','a','n','d',' ','M',' ','c','o','l','u','m','n','s',' ','t','o',' ','d','i','s','p','l','a','y',' ','f','i','l','e',' ','h','e','a','d','e','r','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','h','e','a','d','e','r','=','[','_','\b','L','[',',','_','\b','C','[',',','_','\b','N',']',']',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','U','s','e',' ','_','\b','L',' ','l','i','n','e','s',' ','(','s','t','a','r','t','i','n','g',' ','a','t',' ','l','i','n','e',' ','_','\b','N',')',' ','a','n','d',' ','_','\b','C',' ','c','o','l','u','m','n','s',' ','a','s',' ','h','e','a','d','e','r','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','i','n','c','s','e','a','r','c','h','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','a','r','c','h',' ','f','i','l','e',' ','a','s',' ','e','a','c','h',' ','p','a','t','t','e','r','n',' ','c','h','a','r','a','c','t','e','r',' ','i','s',' ','t','y','p','e','d',' ','i','n','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','i','n','t','r','=','_','\b','C','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','i','n','t','r','=','[','_','\b','C',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','U','s','e',' ','_','\b','C',' ','i','n','s','t','e','a','d',' ','o','f',' ','^','X',' ','t','o',' ','i','n','t','e','r','r','u','p','t',' ','a',' ','r','e','a','d','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','l','i','n','e','-','n','u','m','-','w','i','d','t','h','=','_','\b','N','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','l','e','s','s','k','e','y','-','c','o','n','t','e','x','t','=','_','\b','t','_','\b','e','_','\b','x','_','\b','t','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','U','s','e',' ','l','e','s','s','k','e','y',' ','s','o','u','r','c','e',' ','f','i','l','e',' ','c','o','n','t','e','n','t','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','l','e','s','s','k','e','y','-','s','r','c','=','_','\b','f','_','\b','i','_','\b','l','_','\b','e','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','U','s','e',' ','a',' ','l','e','s','s','k','e','y',' ','s','o','u','r','c','e',' ','f','i','l','e','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','l','i','n','e','-','n','u','m','-','w','i','d','t','h','=','[','_','\b','N',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','t',' ','t','h','e',' ','w','i','d','t','h',' ','o','f',' ','t','h','e',' ','-','N',' ','l','i','n','e',' ','n','u','m','b','e','r',' ','f','i','e','l','d',' ','t','o',' ','_','\b','N',' ','c','h','a','r','a','c','t','e','r','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','m','o','d','e','l','i','n','e','s','=','_','\b','N','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','m','a','t','c','h','-','s','h','i','f','t','=','[','_','\b','N',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','h','o','w',' ','a','t',' ','l','e','a','s','t',' ','_','\b','N',' ','c','h','a','r','a','c','t','e','r','s',' ','t','o',' ','t','h','e',' ','l','e','f','t',' ','o','f',' ','a',' ','s','e','a','r','c','h',' ','m','a','t','c','h','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','m','o','d','e','l','i','n','e','s','=','[','_','\b','N',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','R','e','a','d',' ','_','\b','N',' ','l','i','n','e','s',' ','f','r','o','m',' ','t','h','e',' ','i','n','p','u','t',' ','f','i','l','e',' ','a','n','d',' ','l','o','o','k',' ','f','o','r',' ','v','i','m',' ','m','o','d','e','l','i','n','e','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','m','o','u','s','e','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','n','a','b','l','e',' ','m','o','u','s','e',' ','i','n','p','u','t','.','\n',
@ -236,13 +248,17 @@ constant char helpdata[] = {
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','R','e','m','o','v','e',' ','d','u','p','l','i','c','a','t','e','s',' ','f','r','o','m',' ','c','o','m','m','a','n','d',' ','h','i','s','t','o','r','y','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','n','o','-','n','u','m','b','e','r','-','h','e','a','d','e','r','s','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','g','i','v','e',' ','l','i','n','e',' ','n','u','m','b','e','r','s',' ','t','o',' ','h','e','a','d','e','r',' ','l','i','n','e','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','n','o','-','s','e','a','r','c','h','-','h','e','a','d','e','r','-','l','i','n','e','s','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','a','r','c','h','e','s',' ','d','o',' ','n','o','t',' ','i','n','c','l','u','d','e',' ','h','e','a','d','e','r',' ','l','i','n','e','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','n','o','-','s','e','a','r','c','h','-','h','e','a','d','e','r','-','c','o','l','u','m','n','s','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','a','r','c','h','e','s',' ','d','o',' ','n','o','t',' ','i','n','c','l','u','d','e',' ','h','e','a','d','e','r',' ','c','o','l','u','m','n','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','n','o','-','s','e','a','r','c','h','-','h','e','a','d','e','r','s','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','s','e','a','r','c','h',' ','i','n',' ','h','e','a','d','e','r',' ','l','i','n','e','s',' ','o','r',' ','c','o','l','u','m','n','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','a','r','c','h','e','s',' ','d','o',' ','n','o','t',' ','i','n','c','l','u','d','e',' ','h','e','a','d','e','r',' ','l','i','n','e','s',' ','o','r',' ','c','o','l','u','m','n','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','n','o','-','v','b','e','l','l','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','i','s','a','b','l','e',' ','t','h','e',' ','t','e','r','m','i','n','a','l','\'','s',' ','v','i','s','u','a','l',' ','b','e','l','l','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','r','e','d','r','a','w','-','o','n','-','q','u','i','t','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','R','e','d','r','a','w',' ','f','i','n','a','l',' ','s','c','r','e','e','n',' ','w','h','e','n',' ','q','u','i','t','t','i','n','g','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','r','s','c','r','o','l','l','=','_','\b','C','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','r','s','c','r','o','l','l','=','[','_','\b','C',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','t',' ','t','h','e',' ','c','h','a','r','a','c','t','e','r',' ','u','s','e','d',' ','t','o',' ','m','a','r','k',' ','t','r','u','n','c','a','t','e','d',' ','l','i','n','e','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','s','a','v','e','-','m','a','r','k','s','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','R','e','t','a','i','n',' ','m','a','r','k','s',' ','a','c','r','o','s','s',' ','i','n','v','o','c','a','t','i','o','n','s',' ','o','f',' ','l','e','s','s','.','\n',
@ -252,17 +268,17 @@ constant char helpdata[] = {
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','i','s','p','l','a','y',' ','a',' ','m','e','s','s','a','g','e',' ','i','f',' ','p','r','e','p','r','o','c','e','s','s','o','r',' ','e','x','i','t','s',' ','w','i','t','h',' ','a','n',' ','e','r','r','o','r',' ','s','t','a','t','u','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','p','r','o','c','-','b','a','c','k','s','p','a','c','e','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','P','r','o','c','e','s','s',' ','b','a','c','k','s','p','a','c','e','s',' ','f','o','r',' ','b','o','l','d','/','u','n','d','e','r','l','i','n','e','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','S','P','E','C','I','A','L','-','B','A','C','K','S','P','A','C','E','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','P','R','O','C','-','B','A','C','K','S','P','A','C','E','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','T','r','e','a','t',' ','b','a','c','k','s','p','a','c','e','s',' ','a','s',' ','c','o','n','t','r','o','l',' ','c','h','a','r','a','c','t','e','r','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','p','r','o','c','-','r','e','t','u','r','n','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','e','l','e','t','e',' ','c','a','r','r','i','a','g','e',' ','r','e','t','u','r','n','s',' ','b','e','f','o','r','e',' ','n','e','w','l','i','n','e','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','S','P','E','C','I','A','L','-','R','E','T','U','R','N','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','P','R','O','C','-','R','E','T','U','R','N','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','T','r','e','a','t',' ','c','a','r','r','i','a','g','e',' ','r','e','t','u','r','n','s',' ','a','s',' ','c','o','n','t','r','o','l',' ','c','h','a','r','a','c','t','e','r','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','p','r','o','c','-','t','a','b','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','x','p','a','n','d',' ','t','a','b','s',' ','t','o',' ','s','p','a','c','e','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','S','P','E','C','I','A','L','-','T','A','B','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','P','R','O','C','-','T','A','B','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','T','r','e','a','t',' ','t','a','b','s',' ','a','s',' ','c','o','n','t','r','o','l',' ','c','h','a','r','a','c','t','e','r','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','s','t','a','t','u','s','-','c','o','l','-','w','i','d','t','h','=','_','\b','N','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','s','t','a','t','u','s','-','c','o','l','-','w','i','d','t','h','=','[','_','\b','N',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','t',' ','t','h','e',' ','w','i','d','t','h',' ','o','f',' ','t','h','e',' ','-','J',' ','s','t','a','t','u','s',' ','c','o','l','u','m','n',' ','t','o',' ','_','\b','N',' ','c','h','a','r','a','c','t','e','r','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','s','t','a','t','u','s','-','l','i','n','e','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','H','i','g','h','l','i','g','h','t',' ','o','r',' ','c','o','l','o','r',' ','t','h','e',' ','e','n','t','i','r','e',' ','l','i','n','e',' ','c','o','n','t','a','i','n','i','n','g',' ','a',' ','m','a','r','k','.','\n',
@ -270,7 +286,7 @@ constant char helpdata[] = {
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','u','b','s','e','q','u','e','n','t',' ','o','p','t','i','o','n','s',' ','u','s','e',' ','b','a','c','k','s','l','a','s','h',' ','a','s',' ','e','s','c','a','p','e',' ','c','h','a','r','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','u','s','e','-','c','o','l','o','r','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','n','a','b','l','e','s',' ','c','o','l','o','r','e','d',' ','t','e','x','t','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','w','h','e','e','l','-','l','i','n','e','s','=','_','\b','N','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','w','h','e','e','l','-','l','i','n','e','s','=','[','_','\b','N',']','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','a','c','h',' ','c','l','i','c','k',' ','o','f',' ','t','h','e',' ','m','o','u','s','e',' ','w','h','e','e','l',' ','m','o','v','e','s',' ','_','\b','N',' ','l','i','n','e','s','.','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','w','o','r','d','w','r','a','p','\n',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','W','r','a','p',' ','l','i','n','e','s',' ','a','t',' ','s','p','a','c','e','s','.','\n',

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -48,7 +48,7 @@ struct ifile {
* Anchor for linked list.
*/
static struct ifile anchor = { &anchor, &anchor, NULL, NULL, NULL, 0, 0, '\0',
{ NULL_POSITION, 0 } };
{ NULL_POSITION, 0 }, NULL, NULL };
static int ifiles = 0;
static void incr_index(struct ifile *p, int incr)
@ -97,7 +97,7 @@ static void unlink_ifile(struct ifile *p)
* (or at the beginning of the list if "prev" is NULL).
* Return a pointer to the new ifile structure.
*/
static struct ifile * new_ifile(char *filename, struct ifile *prev)
static struct ifile * new_ifile(constant char *filename, struct ifile *prev)
{
struct ifile *p;
@ -197,7 +197,7 @@ public int nifile(void)
/*
* Find an ifile structure, given a filename.
*/
static struct ifile * find_ifile(char *filename)
static struct ifile * find_ifile(constant char *filename)
{
struct ifile *p;
char *rfilename = lrealpath(filename);
@ -229,7 +229,7 @@ static struct ifile * find_ifile(char *filename)
* If the filename has not been seen before,
* insert the new ifile after "prev" in the list.
*/
public IFILE get_ifile(char *filename, IFILE prev)
public IFILE get_ifile(constant char *filename, IFILE prev)
{
struct ifile *p;
@ -241,7 +241,7 @@ public IFILE get_ifile(char *filename, IFILE prev)
/*
* Get the display filename associated with a ifile.
*/
public char * get_filename(IFILE ifile)
public constant char * get_filename(IFILE ifile)
{
if (ifile == NULL)
return (NULL);
@ -251,7 +251,7 @@ public char * get_filename(IFILE ifile)
/*
* Get the canonical filename associated with a ifile.
*/
public char * get_real_filename(IFILE ifile)
public constant char * get_real_filename(IFILE ifile)
{
if (ifile == NULL)
return (NULL);
@ -337,6 +337,10 @@ public void set_altfilename(IFILE ifile, char *altfilename)
p->h_altfilename = altfilename;
}
/*
* Not constant; caller can free altfilename.
* {{ This is poor design and should be changed. }}
*/
public char * get_altfilename(IFILE ifile)
{
return (int_ifile(ifile)->h_altfilename);

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -22,7 +22,6 @@
extern int squeeze;
extern int hshift;
extern int quit_if_one_screen;
extern int sigs;
extern int ignore_eoi;
extern int status_col;
extern int wordwrap;
@ -30,7 +29,7 @@ extern POSITION start_attnpos;
extern POSITION end_attnpos;
#if HILITE_SEARCH
extern int hilite_search;
extern int size_linebuf;
extern size_t size_linebuf;
extern int show_attn;
#endif
@ -47,7 +46,7 @@ static void init_status_col(POSITION base_pos, POSITION disp_pos, POSITION edisp
{
int hl_before = (chop_line() && disp_pos != NULL_POSITION) ?
is_hilited_attr(base_pos, disp_pos, TRUE, NULL) : 0;
int hl_after = (chop_line()) ?
int hl_after = (chop_line() && edisp_pos != NULL_POSITION) ?
is_hilited_attr(edisp_pos, eol_pos, TRUE, NULL) : 0;
int attr;
char ch;
@ -64,11 +63,14 @@ static void init_status_col(POSITION base_pos, POSITION disp_pos, POSITION edisp
{
attr = hl_after;
ch = '>';
} else
} else if (disp_pos != NULL_POSITION)
{
attr = is_hilited_attr(base_pos, eol_pos, TRUE, NULL);
attr = is_hilited_attr(disp_pos, edisp_pos, TRUE, NULL);
ch = '*';
}
} else
{
attr = 0;
}
if (attr)
set_status_col(ch, attr);
}
@ -80,18 +82,18 @@ static void init_status_col(POSITION base_pos, POSITION disp_pos, POSITION edisp
* a line. The new position is the position of the first character
* of the NEXT line. The line obtained is the line starting at curr_pos.
*/
public POSITION forw_line_seg(POSITION curr_pos, int skipeol, int rscroll, int nochop)
public POSITION forw_line_seg(POSITION curr_pos, lbool skipeol, lbool rscroll, lbool nochop)
{
POSITION base_pos;
POSITION new_pos;
POSITION edisp_pos;
int c;
int blankline;
int endline;
int chopped;
lbool blankline;
lbool endline;
lbool chopped;
int backchars;
POSITION wrap_pos;
int skipped_leading;
lbool skipped_leading;
get_forw_line:
if (curr_pos == NULL_POSITION)
@ -109,8 +111,7 @@ get_forw_line:
* If we're not ignoring EOI, we *could* do the same, but
* for efficiency we prepare several lines ahead at once.
*/
prep_hilite(curr_pos, curr_pos + 3*size_linebuf,
ignore_eoi ? 1 : -1);
prep_hilite(curr_pos, curr_pos + (POSITION) (3*size_linebuf), ignore_eoi ? 1 : -1);
curr_pos = next_unfiltered(curr_pos);
}
#endif
@ -126,11 +127,6 @@ get_forw_line:
base_pos = curr_pos;
for (;;)
{
if (ABORT_SIGS())
{
null_line();
return (NULL_POSITION);
}
c = ch_back_get();
if (c == EOI)
break;
@ -151,13 +147,13 @@ get_forw_line:
new_pos = base_pos;
while (new_pos < curr_pos)
{
if (ABORT_SIGS())
c = ch_forw_get();
if (c == EOI)
{
null_line();
return (NULL_POSITION);
}
c = ch_forw_get();
backchars = pappend(c, new_pos);
backchars = pappend((char) c, new_pos);
new_pos++;
if (backchars > 0)
{
@ -167,7 +163,7 @@ get_forw_line:
do
{
new_pos++;
c = ch_forw_get();
c = ch_forw_get(); /* {{ what if c == EOI? }} */
} while (c == ' ' || c == '\t');
backchars = 1;
}
@ -198,11 +194,6 @@ get_forw_line:
chopped = FALSE;
for (;;)
{
if (ABORT_SIGS())
{
null_line();
return (NULL_POSITION);
}
if (c == '\n' || c == EOI)
{
/*
@ -220,12 +211,12 @@ get_forw_line:
break;
}
if (c != '\r')
blankline = 0;
blankline = FALSE;
/*
* Append the char to the line and get the next char.
*/
backchars = pappend(c, ch_tell()-1);
backchars = pappend((char) c, ch_tell()-1);
if (backchars > 0)
{
/*
@ -236,14 +227,9 @@ get_forw_line:
if (skipeol)
{
/* Read to end of line. */
edisp_pos = ch_tell();
edisp_pos = ch_tell() - backchars;
do
{
if (ABORT_SIGS())
{
null_line();
return (NULL_POSITION);
}
c = ch_forw_get();
} while (c != '\n' && c != EOI);
new_pos = ch_tell();
@ -266,10 +252,10 @@ get_forw_line:
do
{
new_pos = ch_tell();
c = ch_forw_get();
c = ch_forw_get(); /* {{ what if c == EOI? }} */
} while (c == ' ' || c == '\t');
if (c == '\r')
c = ch_forw_get();
c = ch_forw_get(); /* {{ what if c == EOI? }} */
if (c == '\n')
new_pos = ch_tell();
} else if (wrap_pos == NULL_POSITION)
@ -281,6 +267,7 @@ get_forw_line:
}
}
endline = FALSE;
edisp_pos = new_pos;
}
break;
}
@ -302,8 +289,10 @@ get_forw_line:
#if HILITE_SEARCH
if (blankline && show_attn)
{
/* Add spurious space to carry possible attn hilite. */
pappend(' ', ch_tell()-1);
/* Add spurious space to carry possible attn hilite.
* Use pappend_b so that if line ended with \r\n,
* we insert the space before the \r. */
pappend_b(' ', ch_tell()-1, TRUE);
}
#endif
pdone(endline, rscroll && chopped, 1);
@ -330,11 +319,7 @@ get_forw_line:
* and pretend it is the one which we are returning.
*/
while ((c = ch_forw_get()) == '\n' || c == '\r')
if (ABORT_SIGS())
{
null_line();
return (NULL_POSITION);
}
continue;
if (c != EOI)
(void) ch_back_get();
new_pos = ch_tell();
@ -363,11 +348,11 @@ public POSITION back_line(POSITION curr_pos)
POSITION edisp_pos;
POSITION begin_new_pos;
int c;
int endline;
int chopped;
lbool endline;
lbool chopped;
int backchars;
POSITION wrap_pos;
int skipped_leading;
lbool skipped_leading;
get_back_line:
if (curr_pos == NULL_POSITION || curr_pos <= ch_zero())
@ -377,8 +362,8 @@ get_back_line:
}
#if HILITE_SEARCH
if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
prep_hilite((curr_pos < 3*size_linebuf) ?
0 : curr_pos - 3*size_linebuf, curr_pos, -1);
prep_hilite((curr_pos < (POSITION) (3*size_linebuf)) ? 0 :
curr_pos - (POSITION) (3*size_linebuf), curr_pos, -1);
#endif
if (ch_seek(curr_pos-1))
{
@ -393,6 +378,7 @@ get_back_line:
*/
(void) ch_forw_get(); /* Skip the newline */
c = ch_forw_get(); /* First char of "current" line */
/* {{ what if c == EOI? }} */
(void) ch_back_get(); /* Restore our position */
(void) ch_back_get();
@ -404,11 +390,7 @@ get_back_line:
* since we skipped them in forw_line().
*/
while ((c = ch_back_get()) == '\n' || c == '\r')
if (ABORT_SIGS())
{
null_line();
return (NULL_POSITION);
}
continue;
if (c == EOI)
{
null_line();
@ -423,11 +405,6 @@ get_back_line:
*/
for (;;)
{
if (ABORT_SIGS())
{
null_line();
return (NULL_POSITION);
}
c = ch_back_get();
if (c == '\n')
{
@ -478,7 +455,7 @@ get_back_line:
for (;;)
{
c = ch_forw_get();
if (c == EOI || ABORT_SIGS())
if (c == EOI)
{
null_line();
return (NULL_POSITION);
@ -496,7 +473,7 @@ get_back_line:
edisp_pos = new_pos;
break;
}
backchars = pappend(c, ch_tell()-1);
backchars = pappend((char) c, ch_tell()-1);
if (backchars > 0)
{
/*
@ -523,24 +500,28 @@ get_back_line:
{
for (;;)
{
c = ch_forw_get();
c = ch_forw_get(); /* {{ what if c == EOI? }} */
if (c == ' ' || c == '\t')
new_pos++;
else
{
if (c == '\r')
{
c = ch_forw_get();
c = ch_forw_get(); /* {{ what if c == EOI? }} */
if (c == '\n')
new_pos++;
}
if (c == '\n')
new_pos++;
edisp_pos = new_pos;
break;
}
}
if (new_pos >= curr_pos)
{
edisp_pos = new_pos;
break;
}
pshift_all();
} else
{

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -16,11 +16,11 @@
#include "position.h"
extern int jump_sline;
extern int squished;
extern int screen_trashed;
extern lbool squished;
extern int sc_width, sc_height;
extern int show_attn;
extern int top_scroll;
extern POSITION header_start_pos;
/*
* Jump to the end of the file.
@ -184,6 +184,38 @@ public void jump_line_loc(POSITION pos, int sline)
jump_loc(pos, sline);
}
static void after_header_message(void)
{
#if HAVE_TIME
#define MSG_FREQ 1 /* seconds */
static time_type last_msg = (time_type) 0;
time_type now = get_time();
if (now < last_msg + MSG_FREQ)
return;
last_msg = now;
#endif
bell();
/* {{ This message displays before the file text is updated, which is not a good UX. }} */
/** error("Cannot display text before header; use --header=- to disable header", NULL_PARG); */
}
/*
* Ensure that a position is not before the header.
* If it is, print a message and return the position of the start of the header.
* {{ This is probably not being used correctly in all cases.
* It does not account for the location of pos on the screen,
* so lines before pos could be displayed. }}
*/
public POSITION after_header_pos(POSITION pos)
{
if (header_start_pos != NULL_POSITION && pos < header_start_pos)
{
after_header_message();
pos = header_start_pos;
}
return pos;
}
/*
* Jump to a specified position in the file.
* The position must be the first character in a line.
@ -199,6 +231,7 @@ public void jump_loc(POSITION pos, int sline)
/*
* Normalize sline.
*/
pos = after_header_pos(pos);
sindex = sindex_from_sline(sline);
if ((nline = onscreen(pos)) >= 0)
@ -214,7 +247,7 @@ public void jump_loc(POSITION pos, int sline)
back(-nline, position(TOP), 1, 0);
#if HILITE_SEARCH
if (show_attn)
repaint_hilite(1);
repaint_hilite(TRUE);
#endif
return;
}
@ -255,7 +288,7 @@ public void jump_loc(POSITION pos, int sline)
forw(sc_height-sindex+nline-1, bpos, 1, 0, 0);
#if HILITE_SEARCH
if (show_attn)
repaint_hilite(1);
repaint_hilite(TRUE);
#endif
return;
}
@ -272,8 +305,8 @@ public void jump_loc(POSITION pos, int sline)
}
}
lastmark();
squished = 0;
screen_trashed = 0;
squished = FALSE;
screen_trashed_num(0);
forw(sc_height-1, pos, 1, 0, sindex-nline);
} else
{
@ -308,7 +341,7 @@ public void jump_loc(POSITION pos, int sline)
back(nline+1, tpos, 1, 0);
#if HILITE_SEARCH
if (show_attn)
repaint_hilite(1);
repaint_hilite(TRUE);
#endif
return;
}
@ -318,7 +351,7 @@ public void jump_loc(POSITION pos, int sline)
clear();
else
home();
screen_trashed = 0;
screen_trashed_num(0);
add_back_pos(pos);
back(sc_height-1, pos, 1, 0);
}

57
contrib/less/lang.h Normal file
View file

@ -0,0 +1,57 @@
/*
* Copyright (C) 1984-2024 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.
*/
#ifndef LESS_LANG_H_
#define LESS_LANG_H_ 1
/*
* C language details.
*/
#if HAVE_CONST
#define constant const
#else
#define constant
#endif
/*
* mutable is the opposite of constant.
* It documents that a pointer parameter will be written through by the
* called function, more directly than by the mere absence of "constant".
*/
#define mutable
#define public /* PUBLIC FUNCTION */
#undef ptr_diff
#define ptr_diff(p1,p2) ((size_t) ((p1)-(p2)))
#undef countof
#define countof(a) ((int)(sizeof(a)/sizeof(*a)))
#define size_t_null ((size_t)-1)
#ifndef NULL
#define NULL 0
#endif
typedef enum lbool { LFALSE, LTRUE } lbool;
#undef TRUE
#define TRUE LTRUE
#undef FALSE
#define FALSE LFALSE
#ifdef _MSC_VER
#if _WIN64
typedef __int64 ssize_t;
#else
typedef __int32 ssize_t;
#endif
#endif
#endif // LESS_LANG_H_

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -36,16 +36,6 @@
#undef HAVE_SIGSETMASK
#endif
/*
* Language details.
*/
#if HAVE_CONST
#define constant const
#else
#define constant
#endif
#define public /* PUBLIC FUNCTION */
/* Library function declarations */
@ -87,11 +77,14 @@
* These substitutes for C23 stdckdint macros do not set *R on overflow,
* and they assume A and B are nonnegative. That is good enough for us.
*/
#define ckd_add(r, a, b) help_ckd_add(r, a, b, sizeof *(r), signed_expr(*(r)))
#define ckd_mul(r, a, b) help_ckd_mul(r, a, b, sizeof *(r), signed_expr(*(r)))
#define ckd_add(r, a, b) help_ckd_add(r, (uintmax)(a), (uintmax)(b), sizeof *(r), signed_expr(*(r)))
#define ckd_mul(r, a, b) help_ckd_mul(r, (uintmax)(a), (uintmax)(b), sizeof *(r), signed_expr(*(r)))
/* True if the integer expression E, after promotion, is signed. */
#define signed_expr(e) ((TRUE ? 0 : e) - 1 < 0)
#endif
#define muldiv(val,num,den) umuldiv((uintmax)(val), (uintmax)(num), (uintmax)(den))
#include "lang.h"
#if defined UINTMAX_MAX
typedef uintmax_t uintmax;
@ -146,21 +139,21 @@ void free();
#undef IS_DIGIT
#if HAVE_WCTYPE
#define IS_UPPER(c) iswupper(c)
#define IS_LOWER(c) iswlower(c)
#define TO_UPPER(c) towupper(c)
#define TO_LOWER(c) towlower(c)
#define IS_UPPER(c) iswupper((wint_t) (c))
#define IS_LOWER(c) iswlower((wint_t) (c))
#define TO_UPPER(c) towupper((wint_t) (c))
#define TO_LOWER(c) towlower((wint_t) (c))
#else
#if HAVE_UPPER_LOWER
#define IS_UPPER(c) isupper((unsigned char) (c))
#define IS_LOWER(c) islower((unsigned char) (c))
#define TO_UPPER(c) toupper((unsigned char) (c))
#define TO_LOWER(c) tolower((unsigned char) (c))
#define IS_UPPER(c) (is_ascii_char(c) && isupper((unsigned char) (c)))
#define IS_LOWER(c) (is_ascii_char(c) && islower((unsigned char) (c)))
#define TO_UPPER(c) (is_ascii_char(c) ? toupper((unsigned char) (c)) : (c))
#define TO_LOWER(c) (is_ascii_char(c) ? tolower((unsigned char) (c)) : (c))
#else
#define IS_UPPER(c) ASCII_IS_UPPER(c)
#define IS_LOWER(c) ASCII_IS_LOWER(c)
#define TO_UPPER(c) ASCII_TO_UPPER(c)
#define TO_LOWER(c) ASCII_TO_LOWER(c)
#define IS_UPPER(c) (is_ascii_char(c) && ASCII_IS_UPPER(c))
#define IS_LOWER(c) (is_ascii_char(c) && ASCII_IS_LOWER(c))
#define TO_UPPER(c) (is_ascii_char(c) ? ASCII_TO_UPPER(c) : (c))
#define TO_LOWER(c) (is_ascii_char(c) ? ASCII_TO_LOWER(c) : (c))
#endif
#endif
@ -178,17 +171,6 @@ void free();
#define IS_CSI_START(c) (((LWCHAR)(c)) == ESC || (((LWCHAR)(c)) == CSI))
#ifndef NULL
#define NULL 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#define OPT_OFF 0
#define OPT_ON 1
#define OPT_ONPLUS 2
@ -236,7 +218,20 @@ void free();
* Special types and constants.
*/
typedef unsigned long LWCHAR;
typedef off_t POSITION;
#if defined(MINGW) || (defined(_MSC_VER) && _MSC_VER >= 1500)
typedef long long less_off_t; /* __int64 */
typedef struct _stat64 less_stat_t;
#define less_fstat _fstat64
#define less_stat _stat64
#define less_lseek _lseeki64
#else
typedef off_t less_off_t;
typedef struct stat less_stat_t;
#define less_fstat fstat
#define less_stat stat
#define less_lseek lseek
#endif
typedef less_off_t POSITION;
typedef off_t LINENUM;
#define MIN_LINENUM_WIDTH 7 /* Default min printing width of a line number */
#define MAX_LINENUM_WIDTH 16 /* Max width of a line number */
@ -327,7 +322,7 @@ struct scrpos
typedef union parg
{
char *p_string;
constant char *p_string;
int p_int;
LINENUM p_linenum;
char p_char;
@ -348,8 +343,8 @@ struct wchar_range
struct wchar_range_table
{
struct wchar_range *table;
int count;
struct wchar_range *table;
unsigned int count;
};
#if HAVE_POLL
@ -397,13 +392,25 @@ typedef short POLL_EVENTS;
#define SRCH_FILTER (1 << 13) /* Search is for '&' (filter) command */
#define SRCH_AFTER_TARGET (1 << 14) /* Start search after the target line */
#define SRCH_WRAP (1 << 15) /* Wrap-around search (continue at BOF/EOF) */
#define SRCH_SUBSEARCH(i) (1 << (16+(i))) /* Search for subpattern */
#if OSC8_LINK
#define SRCH_OSC8 (1 << 16) /* */
#endif
#define SRCH_SUBSEARCH(i) (1 << (17+(i))) /* Search for subpattern */
/* {{ Depends on NUM_SEARCH_COLORS==5 }} */
#define SRCH_SUBSEARCH_ALL (SRCH_SUBSEARCH(1)|SRCH_SUBSEARCH(2)|SRCH_SUBSEARCH(3)|SRCH_SUBSEARCH(4)|SRCH_SUBSEARCH(5))
#define SRCH_REVERSE(t) (((t) & SRCH_FORW) ? \
(((t) & ~SRCH_FORW) | SRCH_BACK) : \
(((t) & ~SRCH_BACK) | SRCH_FORW))
/* Parsing position in an OSC8 link: "\e]8;PARAMS;URI\e\\" (final "\e\\" may be "\7") */
typedef enum osc8_state {
OSC8_NOT, /* This is not an OSC8 link */
OSC8_PREFIX, /* In the "\e]8;" */
OSC8_PARAMS, /* In the parameters */
OSC8_URI, /* In the URI */
OSC8_ST_ESC, /* After the final \e */
OSC8_END, /* At end */
} osc8_state;
/* */
#define NO_MCA 0
@ -454,10 +461,21 @@ typedef enum {
CV_ERROR = -1
} COLOR_VALUE;
typedef enum {
CATTR_NULL = 0,
CATTR_STANDOUT = (1 << 0),
CATTR_BOLD = (1 << 1),
CATTR_UNDERLINE = (1 << 2),
CATTR_BLINK = (1 << 3),
} CHAR_ATTR;
/* ANSI states */
#define ANSI_MID 1
#define ANSI_ERR 2
#define ANSI_END 3
typedef enum {
ANSI_NULL,
ANSI_MID,
ANSI_ERR,
ANSI_END,
} ansi_state;
#if '0' == 240
#define IS_EBCDIC_HOST 1
@ -534,7 +552,6 @@ typedef enum {
#define ESC CONTROL('[')
#define ESCS "\33"
#define CSI ((unsigned char)'\233')
#define CHAR_END_COMMAND 0x40000000
#if _OSK_MWC32
#define LSIGNAL(sig,func) os9_signal(sig,func)
@ -584,6 +601,7 @@ typedef enum {
#define CH_POPENED 004
#define CH_HELPFILE 010
#define CH_NODATA 020 /* Special case for zero length files */
#define CH_NOTRUSTSIZE 040 /* For files that claim 0 length size falsely */
#define ch_zero() ((POSITION)0)
@ -611,6 +629,20 @@ typedef enum {
#define X11MOUSE_WHEEL_DOWN 0x41 /* Wheel scroll down */
#define X11MOUSE_OFFSET 0x20 /* Added to button & pos bytes to create a char */
/* Security features. */
#define SF_EDIT (1<<1) /* Edit file (v) */
#define SF_EXAMINE (1<<2) /* Examine file (:e) */
#define SF_GLOB (1<<3) /* Expand file pattern */
#define SF_HISTORY (1<<4) /* History file */
#define SF_LESSKEY (1<<5) /* Lesskey files */
#define SF_LESSOPEN (1<<6) /* LESSOPEN */
#define SF_LOGFILE (1<<7) /* Log file (s, -o) */
#define SF_PIPE (1<<8) /* Pipe (|) */
#define SF_SHELL (1<<9) /* Shell command (!) */
#define SF_STOP (1<<10) /* Stop signal */
#define SF_TAGS (1<<11) /* Tags */
#define SF_OSC8_OPEN (1<<12) /* OSC8 open */
#if LESSTEST
#define LESS_DUMP_CHAR CONTROL(']')
#endif
@ -630,6 +662,9 @@ void inttoa(int, char*, int);
int lstrtoi(char*, char**, int);
POSITION lstrtopos(char*, char**, int);
unsigned long lstrtoul(char*, char**, int);
int lstrtoic(constant char*, constant char**, int);
POSITION lstrtoposc(constant char*, constant char**, int);
unsigned long lstrtoulc(constant char*, constant char**, int);
#if MSDOS_COMPILER==WIN32C
int pclose(FILE*);
#endif

View file

@ -41,6 +41,9 @@
N * Repeat previous search in reverse direction.
ESC-n * Repeat previous search, spanning files.
ESC-N * Repeat previous search, reverse dir. & spanning files.
^O^N ^On * Search forward for (_N-th) OSC8 hyperlink.
^O^P ^Op * Search backward for (_N-th) OSC8 hyperlink.
^O^L ^Ol Jump to the currently selected OSC8 hyperlink.
ESC-u Undo (toggle) search highlighting.
ESC-U Clear search highlighting.
&_p_a_t_t_e_r_n * Display only matching lines.
@ -53,6 +56,7 @@
^R Don't use REGULAR EXPRESSIONS.
^S _n Search for match in _n-th parenthesized subpattern.
^W WRAP search if no match found.
^L Enter next character literally into pattern.
---------------------------------------------------------------------------
JJUUMMPPIINNGG
@ -92,6 +96,7 @@
:n * Examine the (_N-th) next file from the command line.
:p * Examine the (_N-th) previous file from the command line.
:x * Examine the first (or _N-th) file from the command line.
^O^O Open the currently selected OSC8 hyperlink.
:d Delete the current file from the command line list.
= ^G :f Print current file name.
---------------------------------------------------------------------------
@ -155,8 +160,8 @@
Screen position of target lines.
-J ........ --status-column
Display a status column at left edge of screen.
-k [_f_i_l_e] . --lesskey-file=[_f_i_l_e]
Use a lesskey file.
-k _f_i_l_e ... --lesskey-file=_f_i_l_e
Use a compiled lesskey file.
-K ........ --quit-on-intr
Exit less in response to ctrl-C.
-L ........ --no-lessopen
@ -167,11 +172,11 @@
Suppress line numbers in prompts and messages.
-N ......... --LINE-NUMBERS
Display line number at start of each line.
-o [_f_i_l_e] . --log-file=[_f_i_l_e]
-o [_f_i_l_e] .. --log-file=[_f_i_l_e]
Copy to log file (standard input only).
-O [_f_i_l_e] . --LOG-FILE=[_f_i_l_e]
-O [_f_i_l_e] .. --LOG-FILE=[_f_i_l_e]
Copy to log file (unconditionally overwrite).
-p [_p_a_t_t_e_r_n] --pattern=[_p_a_t_t_e_r_n]
-p _p_a_t_t_e_r_n . --pattern=[_p_a_t_t_e_r_n]
Start at pattern (from command line).
-P [_p_r_o_m_p_t] --prompt=[_p_r_o_m_p_t]
Define new prompt.
@ -183,7 +188,7 @@
Squeeze multiple blank lines.
-S ........ --chop-long-lines
Chop (truncate) long lines rather than wrapping.
-t [_t_a_g] .. --tag=[_t_a_g]
-t _t_a_g .... --tag=[_t_a_g]
Find a tag.
-T [_t_a_g_s_f_i_l_e] --tag-file=[_t_a_g_s_f_i_l_e]
Use an alternate tags file.
@ -209,21 +214,28 @@
Don't display tildes after end of file.
-# [_N] .... --shift=[_N]
Set horizontal scroll amount (0 = one half screen width).
--exit-follow-on-close
Exit F command on a pipe when writer closes pipe.
--file-size
Automatically determine the size of the input file.
--follow-name
The F command changes files if the input file is renamed.
--header=[_N[,_M]]
Use N lines and M columns to display file headers.
--header=[_L[,_C[,_N]]]
Use _L lines (starting at line _N) and _C columns as headers.
--incsearch
Search file as each pattern character is typed in.
--intr=_C
--intr=[_C]
Use _C instead of ^X to interrupt a read.
--line-num-width=_N
--lesskey-context=_t_e_x_t
Use lesskey source file contents.
--lesskey-src=_f_i_l_e
Use a lesskey source file.
--line-num-width=[_N]
Set the width of the -N line number field to _N characters.
--modelines=_N
--match-shift=[_N]
Show at least _N characters to the left of a search match.
--modelines=[_N]
Read _N lines from the input file and look for vim modelines.
--mouse
Enable mouse input.
@ -233,13 +245,17 @@
Remove duplicates from command history.
--no-number-headers
Don't give line numbers to header lines.
--no-search-header-lines
Searches do not include header lines.
--no-search-header-columns
Searches do not include header columns.
--no-search-headers
Don't search in header lines or columns.
Searches do not include header lines or columns.
--no-vbell
Disable the terminal's visual bell.
--redraw-on-quit
Redraw final screen when quitting.
--rscroll=_C
--rscroll=[_C]
Set the character used to mark truncated lines.
--save-marks
Retain marks across invocations of less.
@ -249,17 +265,17 @@
Display a message if preprocessor exits with an error status.
--proc-backspace
Process backspaces for bold/underline.
--SPECIAL-BACKSPACE
--PROC-BACKSPACE
Treat backspaces as control characters.
--proc-return
Delete carriage returns before newline.
--SPECIAL-RETURN
--PROC-RETURN
Treat carriage returns as control characters.
--proc-tab
Expand tabs to spaces.
--SPECIAL-TAB
--PROC-TAB
Treat tabs as control characters.
--status-col-width=_N
--status-col-width=[_N]
Set the width of the -J status column to _N characters.
--status-line
Highlight or color the entire line containing a mark.
@ -267,7 +283,7 @@
Subsequent options use backslash as escape char.
--use-color
Enables colored text.
--wheel-lines=_N
--wheel-lines=[_N]
Each click of the mouse wheel moves _N lines.
--wordwrap
Wrap lines at spaces.

View file

@ -1,7 +1,7 @@
'\" t
.TH LESS 1 "Version 643: 20 Jul 2023"
.TH LESS 1 "Version 668: 06 Oct 2024"
.SH NAME
less \- opposite of more
less \- display the contents of a file in a terminal
.SH SYNOPSIS
.B "less \-?"
.br
@ -27,10 +27,8 @@ less \- opposite of more
.B Less
is a program similar to
.BR more (1),
but which allows backward movement
in the file as well as forward movement.
Also,
.B less
but it has many more features.
.B Less
does not have to read the entire input file before starting,
so with large input files it starts up faster than text editors like
.BR vi (1).
@ -253,6 +251,9 @@ That is, if the search reaches the end of the current file
without finding a match, the search continues from the first line of the
current file up to the line where it started.
If the \(haW modifier is set, the \(haE modifier is ignored.
.IP "\(haL"
The next character is taken literally; that is, it becomes part of the pattern
even if it is one of the above search modifier characters.
.RE
.IP ?pattern
Search backward in the file for the N-th line containing the pattern.
@ -376,6 +377,12 @@ Go to the next tag, if there were more than one matches for the current tag.
See the \-t option for more details about tags.
.IP "T"
Go to the previous tag, if there were more than one matches for the current tag.
.IP "\(haO\(haN or \(haOn"
Search forward in the file for the N-th next OSC 8 hyperlink.
.IP "\(haO\(haP or \(haOp"
Search backward in the file for the N-th previous OSC 8 hyperlink.
.IP "\(haO\(haL or \(haOl"
Jump to the currently selected OSC 8 hyperlink.
.IP "= or \(haG or :f"
Prints some information about the file being viewed,
including its name
@ -443,7 +450,7 @@ Exits
.BR less .
.PP
The following
six
seven
commands may or may not be valid, depending on your particular installation.
.
.IP v
@ -459,9 +466,11 @@ current file.
A pound sign (#) is replaced by the name of the previously examined file.
"!!" repeats the last shell command.
"!" with no shell command simply invokes a shell.
If a \(haP (CONTROL-P) is entered immediately after the !,
no "done" message is printed after the shell command is executed.
On Unix systems, the shell is taken from the environment variable SHELL,
or defaults to "sh".
On MS-DOS and OS/2 systems, the shell is the normal command processor.
On MS-DOS, Windows, and OS/2 systems, the shell is the normal command processor.
.IP "# shell-command"
Similar to the "!" command,
except that the command is expanded in the same way as prompt strings.
@ -475,9 +484,74 @@ The entire current screen is included, regardless of whether the
marked position is before or after the current screen.
<m> may also be \(ha or $ to indicate beginning or end of file respectively.
If <m> is \&.\& or newline, the current screen is piped.
If a \(haP (CONTROL-P) is entered immediately after the mark letter,
no "done" message is printed after the shell command is executed.
.IP "s filename"
Save the input to a file.
This works only if the input is a pipe, not an ordinary file.
.IP "\(haO\(haO"
.RS
Run a shell command to open the URI in the current OSC 8 hyperlink,
selected by a previous \(haO\(haN or \(haO\(haP command.
To find the shell command,
the environment variable named "LESS_OSC8_xxx" is read,
where "xxx" is the scheme from the URI (the part before the first colon),
or is empty if there is no colon in the URI.
The value of the environment variable is then expanded in the same way as
prompt strings (in particular, any instance of "%o" is replaced with the URI)
to produce an OSC 8 "handler" shell command.
The standard output from the handler is an "opener" shell command
which is then executed to open the URI.
.PP
There are two special cases:
.RS
.IP 1.
If the URI begins with "#", the remainder of the URI is taken to be
the value of the id parameter in another OSC 8 link in the same file,
and \(haO\(haO will simply jump to that link.
.IP 2.
If the opener begins with the characters ":e" followed by
whitespace and a filename,
then instead of running the opener as a shell command,
the specified filename is opened in the current instance of
.BR less .
.RE
.PP
In a simple case where the opener accepts the complete URI
as a command line parameter, the handler may be as simple as
.nf
.sp
echo mybrowser '%o'
.sp
.fi
In other cases, the URI may need to be modified, so the handler
may have to do some manipulation of the %o value.
.PP
If the LESS_OSC8_xxx variable is not set, the variable LESS_OSC8_ANY is tried.
If neither LESS_OSC8_xxx nor LESS_OSC8_ANY is set,
links using the "xxx" scheme cannot be opened.
However, there are default handlers for the
schemes "man" (used when LESS_OSC8_man is not set)
and "file" (used when LESS_OSC8_file is not set),
which should work on systems which provide the
.BR sed (1)
command and a shell with syntax compatible with the Bourne shell
.BR sh (1).
If you use LESS_OSC8_ANY to override LESS_OSC8_file, you must
set LESS_OSC8_file to "-" to indicate that the default value
should not be used, and likewise for LESS_OSC8_man.
.PP
The URI passed to an OSC8 handler via %o is guaranteed not to contain any single quote
or double quote characters, but it may contain any other shell metacharacters
such as semicolons, dollar signs, ampersands, etc.
The handler should take care to appropriately quote parameters in the opener command,
to prevent execution of unintended shell commands in the case of opening
a URI which contains shell metacharacters.
Also, since the handler command is expanded like a command prompt,
any metacharacters interpreted by prompt expansion
(such as percent, dot, colon, backslash, etc.) must be escaped with a backslash
(see the PROMPTS section for details).
.RE
.IP "\(haX"
When the "Waiting for data" message is displayed,
such as while in the F command, pressing \(haX
@ -530,8 +604,9 @@ or if you use
.sp
LESS="\-options"; export LESS
.sp
On MS-DOS, you don't need the quotes, but you should replace any
percent signs in the options string by double percent signs.
On MS-DOS and Windows, you don't need the quotes, but you should
be careful that any percent signs in the options string are not
interpreted as an environment variable expansion.
.sp
The environment variable is parsed before the command line,
so command line options override the LESS environment variable.
@ -543,7 +618,7 @@ Some options like \-k or \-D require a string to follow the option letter.
The string for that option is considered to end when a dollar sign ($) is found.
For example, you can set two \-D options like this:
.sp
LESS="Dn9.1$Ds4.1"
LESS="Dnwb$Dsbw"
.sp
If the \-\-use-backslash option appears earlier in the options, then
a dollar sign or backslash may be included literally in an option string
@ -637,6 +712,8 @@ Prompts.
The rscroll character.
.IP "S"
Search results.
.IP "W"
The highlight enabled via the \-w option.
.IP "1-5"
The text in a search result which matches
the first through fifth parenthesized sub-pattern.
@ -644,8 +721,6 @@ Sub-pattern coloring works only if
.B less
is built with one of the regular expression libraries
.BR posix ", " pcre ", or " pcre2 .
.IP "W"
The highlight enabled via the \-w option.
.IP "d"
Bold text.
.IP "k"
@ -655,7 +730,7 @@ Standout text.
.IP "u"
Underlined text.
.RE
.RS
The uppercase letters and digits can be used only when the \-\-use-color option is enabled.
When text color is specified by both an uppercase letter and a lowercase letter,
@ -663,9 +738,11 @@ the uppercase letter takes precedence.
For example, error messages are normally displayed as standout text.
So if both "s" and "E" are given a color, the "E" color applies
to error messages, and the "s" color applies to other standout text.
The "d" and "u" letters refer to bold and underline text formed by
overstriking with backspaces (see the \-U option),
not to text using ANSI escape sequences with the \-R option.
The lowercase letters refer to bold and underline text formed by
overstriking with backspaces (see the \-U option) and to non-content
text (such as line numbers and prompts),
but not to text formatted using ANSI escape sequences with the \-R option
(but see the note below for different behavior on Windows and MS-DOS).
.PP
A lowercase letter may be followed by a + to indicate that
the normal format change and the specified color should both be used.
@ -675,7 +752,7 @@ But \-Du+g displays underlined text as both green and in underlined format.
.PP
\fIcolor\fP is either a 4-bit color string or an 8-bit color string:
.PP
A 4-bit color string is zero, one or two characters, where
A 4-bit color string is one or two characters, where
the first character specifies the foreground color and
the second specifies the background color as follows:
.IP "b"
@ -707,26 +784,62 @@ where the first integer specifies the foreground color and
the second specifies the background color.
Each integer is a value between 0 and 255 inclusive which selects
a "CSI 38;5" color value (see
.br
.nh
https://en.wikipedia.org/wiki/ANSI_escape_code#SGR)
https://en.wikipedia.org/wiki/ANSI_escape_code#SGR).
.hy
If either integer is a "-" or is omitted,
the corresponding color is set to that of normal text.
On MS-DOS versions of
.BR less ,
8-bit color is not supported; instead, decimal values are interpreted as 4-bit
CHAR_INFO.Attributes values
(see
.br
.nh
https://docs.microsoft.com/en-us/windows/console/char-info-str).
.hy
.PP
On MS-DOS only, the \-Da option may be used to specify strict parsing of
ANSI color (SGR) sequences when the \-R option is used.
Without this option, sequences that change text attributes
(bold, underline, etc.) may clear the text color.
A 4-bit or 8-bit color string may be followed by one or more of the
following characters to set text attributes in addition to the color.
.IP "s or ~"
Standout (reverse video)
.IP "u or _"
Underline
.IP "d or *"
Bold
.IP "l or &"
Blinking
.PP
On MS-DOS and Windows, the \-\-color option behaves
differently from what is described above in these ways:
.IP \(bu
The bold (d and *) and blinking (l and &) text attributes
at the end of a color string are not supported.
.IP \(bu
Lowercase color selector letters refer to text formatted by ANSI
escape sequences with \-R,
in addition to overstruck and non-content text (but see \-Da).
.IP \(bu
For historical reasons, when a lowercase color selector letter
is followed by a numeric color value,
the number is not interpreted as an "CSI 38;5" color value as described above,
but instead as a 4-bit
.nh
CHAR_INFO.Attributes
.hy
value, between 0 and 15 inclusive
(see
.nh
https://learn.microsoft.com/en-us/windows/console/char-info-str).
.hy
To avoid confusion, it is recommended that the equivalent letters rather than numbers
be used after a lowercase color selector on MS-DOS/Windows.
.IP \(bu
Numeric color values ("CSI 38;5" color) following an uppercase color selector letter
are not supported on systems earlier than Windows 10.
.IP \(bu
Only a limited set of ANSI escape sequences to set color in the content work correctly.
4-bit color sequences work, but "CSI 38;5" color sequences do not.
.IP \(bu
The \-Da option makes the behavior of \-\-color
more similar to its behavior on non-MS-DOS/Windows systems by (1)
making lowercase color selector letters not affect text formatted
with ANSI escape sequences, and (2)
allowing "CSI 38;5" color sequences in the content
work by passing them to the terminal (only on Windows 10 and later; on
earlier Windows systems, such sequences do not work regardless of the setting of \-Da).
.RE
.IP "\-e or \-\-quit-at-eof"
Causes
@ -797,6 +910,13 @@ of the screen, starting with a decimal point: \&.5 is in the middle of the
screen, \&.3 is three tenths down from the first line, and so on.
If the line is specified as a fraction, the actual line number
is recalculated if the terminal window is resized.
If the \-\-header option is used and the target line specified by \-j
would be obscured by the header, the target line is moved to the first
line after the header.
While the \-\-header option is active, the \-S option is ignored,
and lines longer than the screen width are truncated.
.RS
.PP
If any form of the \-j option is used,
repeated forward searches (invoked with "n" or "N")
begin at the line immediately after the target line,
@ -807,6 +927,7 @@ fourth line on the screen, so forward searches begin at the fifth line
on the screen.
However nonrepeated searches (invoked with "/" or "?")
always begin at the start or end of the current screen respectively.
.RE
.IP "\-J or \-\-status-column"
Displays a status column at the left edge of the screen.
The character displayed in the status column may be one of:
@ -840,6 +961,7 @@ if a lesskey file is found in a standard place (see KEY BINDINGS),
it is also used as a
.B lesskey
file.
Note the warning under "\-\-lesskey-content" below.
.IP "\-\-lesskey-src=\fIfilename\fP"
Causes
.B less
@ -865,6 +987,26 @@ Newer versions of
read the
.I "lesskey source"
file directly and ignore the binary file if the source file exists.
Note the warning under "\-\-lesskey-content" below.
.IP "\-\-lesskey-content=\fItext\fP"
Causes less to interpret the specified text as the contents of a
.BR lesskey (1)
source file.
In the text,
.B lesskey
lines may be separated by either newlines as usual, or by semicolons.
A literal semicolon may be represented by a backslash followed by a semicolon.
.sp
Warning: certain environment variables such as
LESS, LESSSECURE, LESSCHARSET and others,
which are used early in startup,
cannot be set in a file specified by a command line option
(\-\-lesskey, \-\-lesskey-src or \-\-lesskey-content). When using a
.B lesskey
file to set environment variables, it is safer to use the
default lesskey file, or to specify the file using the
LESSKEYIN or LESSKEY_CONTENT environment variables rather than using
a command line option.
.IP "\-K or \-\-quit-on-intr"
Causes
.B less
@ -1186,25 +1328,32 @@ If the reopen succeeds and the file is a different file from the original
with the same name as the original (now renamed) file),
.B less
will display the contents of that new file.
.IP "\-\-header=\fIN[,M]\fP"
.IP "\-\-header=\fIL\fP,\fIC\fP,\fIN\fP"
.RS
Sets the number of header lines and columns displayed on the screen.
The value may be of the form "N,M" where N and M are integers,
to set the header lines to N and the header columns to M,
or it may be a single integer "N" which sets the header lines to N
and the header columns to zero,
or it may be ",M" which sets the header columns to M and the
header lines to zero.
When N is nonzero, the first N lines at the top
of the screen are replaced with the first N lines of the file,
regardless of what part of the file are being viewed.
When M is nonzero, the characters displayed at the
beginning of each line are replaced with the first M characters of the line,
The number of header lines is set to \fIL\fP.
If \fIL\fP is 0, header lines are disabled.
If \fIL\fP is empty or missing, the number of header lines is unchanged.
The number of header columns is set to \fIC\fP.
If \fIC\fP is 0, header columns are disabled.
If \fIC\fP is empty or missing, the number of header columns is unchanged.
The first header line is set to line number \fIN\fP in the file.
If \fIN\fP is empty or missing, it is taken to be
the number of the line currently displayed in the first line of the screen
(if the \-\-header command has been issued from within
.BR less "),"
or 1 (if the \-\-header option has been given on the command line).
The special form "\-\-header=\-" disables header lines and header columns,
and is equivalent to "\-\-header=0,0".
.PP
When \fIL\fP is nonzero, the first \fIL\fP lines at the top
of the screen are replaced with the \fIL\fP lines of the file beginning at line \fIN\fP,
regardless of what part of the file is being viewed.
When header lines are displayed, any file contents before the header line cannot be viewed.
When \fIC\fP is nonzero, the first \fIC\fP characters displayed at the
beginning of each line are replaced with the first \fIC\fP characters of the line,
even if the rest of the line is scrolled horizontally.
If either N or M is zero,
.B less
stops displaying header lines or columns, respectively.
(Note that it may be necessary to change the setting of the \-j option
to ensure that the target line is not obscured by the header line(s).)
.RE
.IP "\-\-incsearch"
Subsequent search commands will be "incremental"; that is,
.B less
@ -1221,6 +1370,21 @@ to specify a control character.
Sets the minimum width of the line number field when the \-N option is in effect
to \fIn\fP characters.
The default is 7.
.IP "\-\-match-shift=\fIn\fP"
When \-S is in effect, if a search match is not visible
because it is shifted to the left or right of the currently
visible screen, the text will horizontally shift
to ensure that the search match is visible.
This option selects the column in which the first character
of the search match will be placed after the shift.
In other words, there will be \fIn\fP characters visible
to the left of the search match.
Alternately, the number may be specified as a fraction of the width
of the screen, starting with a decimal point: \&.5 is half of the
screen width, \&.3 is three tenths of the screen width, and so on.
If the number is specified as a fraction, the actual number of
scroll positions is recalculated if the terminal window is resized.
.IP "\-\-modelines=\fIn\fP"
.RS
Before displaying a file,
@ -1252,8 +1416,12 @@ See the \-\-tabs description for acceptable values of \fIn\fP.
Enables mouse input:
scrolling the mouse wheel down moves forward in the file,
scrolling the mouse wheel up moves backwards in the file,
and clicking the mouse sets the "#" mark to the line
where the mouse is clicked.
left-click sets the "#" mark to the line where the mouse is clicked,
and right-click (or any other) returns to the "#" mark position.
If a left-click is performed with the mouse cursor on an OSC 8 hyperlink,
the hyperlink is selected as if by the \(haO\(haN command.
If a left-click is performed with the mouse cursor on an OSC 8 hyperlink
which is already selected, the hyperlink is opened as if by the \(haO\(haO command.
The number of lines to scroll when the wheel is moved
can be set by the \-\-wheel-lines option.
Mouse input works only on terminals which support X11 mouse reporting,
@ -1276,6 +1444,10 @@ Normally, a string may appear multiple times.
.IP "\-\-no-number-headers"
Header lines (defined via the \-\-header option) are not assigned line numbers.
Line number 1 is assigned to the first line after any header lines.
.IP "\-\-no-search-header-lines"
Searches do not include header lines, but still include header columns.
.IP "\-\-no-search-header-columns"
Searches do not include header columns, but still include header lines.
.IP "\-\-no-search-headers"
Searches do not include header lines or header columns.
.IP "\-\-no-vbell"
@ -1363,9 +1535,7 @@ This allows a dollar sign to be included in option strings.
Enables colored text in various places.
The \-D option can be used to change the colors.
Colored text works only if the terminal supports
ANSI color escape sequences (as defined in ECMA-48 SGR;
see
.br
ANSI color escape sequences (as defined in
.nh
https://www.ecma-international.org/publications-and-standards/standards/ecma-48).
.hy
@ -1449,7 +1619,7 @@ If it matches more than one filename, the first match
is entered into the command line.
Repeated TABs will cycle thru the other matching filenames.
If the completed filename is a directory, a "/" is appended to the filename.
(On MS-DOS systems, a "\e" is appended.)
(On MS-DOS and Windows systems, a "\e" is appended.)
The environment variable LESSSEPARATOR can be used to specify a
different character to append to a directory name.
.IP "BACKTAB [ ESC-TAB ]"
@ -1458,7 +1628,7 @@ Like, TAB, but cycles in the reverse direction thru the matching filenames.
Complete the partial filename to the left of the cursor.
If it matches more than one filename, all matches are entered into
the command line (if they fit).
.IP "\(haU (Unix and OS/2) or ESC (MS-DOS)"
.IP "\(haU (Unix and OS/2) or ESC (MS-DOS and Windows)"
Delete the entire command line,
or cancel the command if the command line is empty.
If you have changed your line-kill character in Unix to something
@ -1741,7 +1911,7 @@ Selects the UTF-8 encoding of the ISO 10646 character set.
UTF-8 is special in that it supports multi-byte characters in the input file.
It is the only character set that supports multi-byte characters.
.IP windows
Selects a character set appropriate for Microsoft Windows (cp 1251).
Selects a character set appropriate for Microsoft Windows (cp 1252).
.PP
In rare cases, it may be desired to tailor
.B less
@ -1794,7 +1964,7 @@ variables.
.PP
Finally, if the
.I setlocale
interface is also not available, the default character set is latin1.
interface is also not available, the default character set is utf-8.
.PP
Control and binary characters are displayed in standout (reverse video).
Each such character is displayed in caret notation if possible
@ -1924,6 +2094,10 @@ The line to be used is determined by the \fIX\fP, as with the %b option.
Replaced by the line number of the last line in the input file.
.IP "%m"
Replaced by the total number of input files.
.IP "%o"
Replaced by the URI of the currently selected OSC 8 hyperlink,
or a question mark if no hyperlink is selected.
This is used by OSC 8 handlers as explained in the \(haO\(haO command description.
.IP "%p\fIX\fP"
Replaced by the percent into the current input file, based on byte offsets.
The line used is determined by the \fIX\fP as with the %b option.
@ -2015,7 +2189,7 @@ Notice how each question mark has a matching period,
and how the % after the %pt
is included literally by escaping it with a backslash.
.sp
?n?f%f\ .?m(%T %i of %m)\ ..?e(END)\ ?x-\ Next\e:\ %x..%t";
?n?f%f\ .?m(%T %i of %m)\ ..?e(END)\ ?x-\ Next\e:\ %x..%t
.sp
This prints the filename if this is the first prompt in a file,
followed by the "file N of N" message if there is more
@ -2063,33 +2237,53 @@ changed to modify this default.
When the environment variable LESSSECURE is set to 1,
.B less
runs in a "secure" mode.
This means these features are disabled:
.RS
.IP "!"
the shell command
.IP "#"
the pshell command
.IP "|"
the pipe command
.IP ":e"
the examine command.
.IP "v"
the editing command
.IP "s \-o"
log files
.IP "\-k"
use of lesskey files
.IP "\-t"
use of tags files
.IP
metacharacters in filenames, such as *
.IP
filename completion (TAB, \(haL)
.IP
In this mode, these features are disabled:
.IP "edit" 10
the edit command (v)
.IP "examine"
the examine command (:e)
.IP "glob"
metacharacters such as * in filenames,
.br
and filename completion (TAB, \(haL)
.IP "history"
history file
.RE
.IP "lesskey"
use of lesskey files (-k and \-\-lesskey-src)
.IP "lessopen"
input preprocessor (LESSOPEN environment variable)
.IP "logfile"
log files (s and \-o)
.IP "osc8"
opening OSC 8 links (\(haO\(haO)
.IP "pipe"
the pipe command (|)
.IP "shell"
the shell and pshell commands (! and #)
.IP "stop"
stopping
.B less
via a SIGSTOP signal
.IP "tags"
use of tags files (-t)
.PP
The LESSSECURE_ALLOW environment variable can be set to a comma-separated list
of names of features which are selectively enabled when LESSSECURE is set.
Each feature name is the first word in each line in the above list.
A feature name may be abbreviated as long as the abbreviation is unambiguous.
For example, if
.nh
LESSSECURE=1
.hy
and
.nh
LESSSECURE_ALLOW=hist,edit
.hy
were set, all of the above features would be disabled
except for history files and the edit command.
.PP
Less can also be compiled to be permanently in "secure" mode.
In that case, the LESSSECURE and LESSSECURE_ALLOW variables are ignored.
.
.SH "COMPATIBILITY WITH MORE"
If the environment variable LESS_IS_MORE is set to 1,
@ -2210,6 +2404,8 @@ file.
Name of the default
.I "lesskey binary"
file. (Not used if "$LESSKEYIN" exists.)
.IP LESSKEY_CONTENT
The value is parsed as if it were the parameter of a \-\-lesskey-content option.
.IP LESSKEYIN_SYSTEM
Name of the default system-wide
.I "lesskey source"
@ -2230,6 +2426,9 @@ Command line to invoke the (optional) input-preprocessor.
.IP LESSSECURE
Runs less in "secure" mode.
See discussion under SECURITY.
.IP LESSSECURE_ALLOW
Enables individual features which are normally disabled by LESSSECURE.
See discussion under SECURITY.
.IP LESSSEPARATOR
String to be appended to a directory name in filename completion.
.IP LESSUTFBINFMT
@ -2264,9 +2463,22 @@ The default is 4000 (4 seconds).
Emulate the
.BR more (1)
command.
.IP LESS_OSC8_xxx
Where "xxx" is a URI scheme such as "http" or "file",
sets an OSC 8 handler for opening OSC 8 links containing a URI with that scheme.
.IP LESS_OSC8_ANY
Sets an OSC 8 handler for opening OSC 8 links for which there is
no specific LESS_OSC8_xxx handler set for the "xxx" scheme.
.IP LESS_TERMCAP_xx
Where "xx" is any two characters, overrides the definition
of the termcap "xx" capability for the terminal.
.IP LESS_UNSUPPORT
A space-separated list of command line options.
These options will be ignored (with no error message) if they appear
on the command line or in the LESS environment variable.
Options listed in LESS_UNSUPPORT can still be changed by the \- and \-\- commands.
Each option in LESS_UNSUPPORT is a dash followed by a single character
option letter, or two dashes followed by a long option name.
.IP LINES
Sets the number of lines on the screen.
Takes precedence over the number of lines specified by the TERM variable.
@ -2280,7 +2492,7 @@ automatically when running in
.BR more "-compatible mode."
.IP PATH
User's search path (used to find a lesskey file
on MS-DOS and OS/2 systems).
on MS-DOS, Windows, and OS/2 systems).
.IP SHELL
The shell used to execute the !\& command, as well as to expand filenames.
.IP TERM
@ -2303,7 +2515,7 @@ Possible location of the history file; see the description of the LESSHISTFILE e
.BR lessecho (1)
.
.SH COPYRIGHT
Copyright (C) 1984-2023 Mark Nudelman
Copyright (C) 1984-2024 Mark Nudelman
.PP
less is part of the GNU project and is free software.
You can redistribute it and/or modify it
@ -2328,8 +2540,13 @@ See the GNU General Public License for more details.
.
Mark Nudelman
.br
Report bugs at https://github.com/gwsw/less/issues.
Report bugs at
.nh
https://github.com/gwsw/less/issues.
.hy
.br
For more information, see the less homepage at
.br
https://greenwoodsoftware.com/less
.nh
https://greenwoodsoftware.com/less.
.hy

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -133,13 +133,13 @@ static long lstrtol(char *s, char **pend, int radix)
return (n);
}
static void add_metachar(int ch)
static void add_metachar(char ch)
{
if (num_metachars+1 >= size_metachars)
{
char *p;
size_metachars = (size_metachars > 0) ? size_metachars*2 : 16;
p = (char *) malloc(size_metachars);
p = (char *) malloc((size_t) size_metachars);
if (p == NULL)
pr_error("Cannot allocate memory");
@ -192,7 +192,7 @@ int main(int argc, char *argv[])
closequote = *++arg;
break;
case 'd':
closequote = lstrtol(++arg, &s, 0);
closequote = (char) lstrtol(++arg, &s, 0);
if (s == arg)
pr_error("Missing number after -d");
break;
@ -203,7 +203,7 @@ int main(int argc, char *argv[])
meta_escape = arg;
break;
case 'f':
meta_escape_buf[0] = lstrtol(++arg, &s, 0);
meta_escape_buf[0] = (char) lstrtol(++arg, &s, 0);
meta_escape_buf[1] = '\0';
meta_escape = meta_escape_buf;
if (s == arg)
@ -213,7 +213,7 @@ int main(int argc, char *argv[])
openquote = *++arg;
break;
case 'p':
openquote = lstrtol(++arg, &s, 0);
openquote = (char) lstrtol(++arg, &s, 0);
if (s == arg)
pr_error("Missing number after -p");
break;
@ -221,7 +221,7 @@ int main(int argc, char *argv[])
add_metachar(*++arg);
break;
case 'n':
add_metachar(lstrtol(++arg, &s, 0));
add_metachar((char) lstrtol(++arg, &s, 0));
if (s == arg)
pr_error("Missing number after -n");
break;
@ -245,6 +245,7 @@ int main(int argc, char *argv[])
return (0);
}
pr_error("Invalid option after --");
return (0);
default:
pr_error("Invalid option letter");
}

View file

@ -1,4 +1,4 @@
.TH LESSECHO 1 "Version 643: 20 Jul 2023"
.TH LESSECHO 1 "Version 668: 06 Oct 2024"
.SH NAME
lessecho \- expand metacharacters
.SH SYNOPSIS
@ -9,6 +9,10 @@ lessecho \- expand metacharacters
is a program that simply echos its arguments on standard output.
But any metacharacter in the output is preceded by an "escape"
character, which by default is a backslash.
.B lessecho
is invoked internally by
.BR less ,
and is not intended to be used directly by humans.
.SH OPTIONS
A summary of options is included below.
.TP

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -87,24 +87,24 @@
#include "lesskey.h"
#include "cmd.h"
char fileheader[] = {
constant char fileheader[] = {
C0_LESSKEY_MAGIC,
C1_LESSKEY_MAGIC,
C2_LESSKEY_MAGIC,
C3_LESSKEY_MAGIC
};
char filetrailer[] = {
constant char filetrailer[] = {
C0_END_LESSKEY_MAGIC,
C1_END_LESSKEY_MAGIC,
C2_END_LESSKEY_MAGIC
};
char cmdsection[1] = { CMD_SECTION };
char editsection[1] = { EDIT_SECTION };
char varsection[1] = { VAR_SECTION };
char endsection[1] = { END_SECTION };
constant char cmdsection[1] = { CMD_SECTION };
constant char editsection[1] = { EDIT_SECTION };
constant char varsection[1] = { VAR_SECTION };
constant char endsection[1] = { END_SECTION };
char *infile = NULL;
char *outfile = NULL ;
constant char *infile = NULL;
constant char *outfile = NULL;
extern char version[];
@ -114,14 +114,14 @@ static void usage(void)
exit(1);
}
void lesskey_parse_error(char *s)
void lesskey_parse_error(constant char *s)
{
fprintf(stderr, "%s\n", s);
}
int lstrtoi(char *buf, char **ebuf, int radix)
int lstrtoi(constant char *buf, constant char **ebuf, int radix)
{
return (int) strtol(buf, ebuf, radix);
return (int) strtol(buf, (char**)ebuf, radix);
}
void out_of_memory(void)
@ -130,7 +130,7 @@ void out_of_memory(void)
exit(1);
}
void * ecalloc(int count, unsigned int size)
void * ecalloc(size_t count, size_t size)
{
void *p;
@ -140,7 +140,7 @@ void * ecalloc(int count, unsigned int size)
return (p);
}
static char * mkpathname(char *dirname, char *filename)
static char * mkpathname(constant char *dirname, constant char *filename)
{
char *pathname;
@ -154,9 +154,9 @@ static char * mkpathname(char *dirname, char *filename)
/*
* Figure out the name of a default file (in the user's HOME directory).
*/
char * homefile(char *filename)
char * homefile(constant char *filename)
{
char *p;
constant char *p;
char *pathname;
if ((p = getenv("HOME")) != NULL && *p != '\0')
@ -176,9 +176,9 @@ char * homefile(char *filename)
/*
* Parse command line arguments.
*/
static void parse_args(int argc, char **argv)
static void parse_args(int argc, constant char **argv)
{
char *arg;
constant char *arg;
outfile = NULL;
while (--argc > 0)
@ -246,7 +246,7 @@ static void parse_args(int argc, char **argv)
/*
* Output some bytes.
*/
static void fputbytes(FILE *fd, char *buf, int len)
static void fputbytes(FILE *fd, constant char *buf, size_t len)
{
while (len-- > 0)
{
@ -258,23 +258,30 @@ static void fputbytes(FILE *fd, char *buf, int len)
/*
* Output an integer, in special KRADIX form.
*/
static void fputint(FILE *fd, unsigned int val)
static void fputint(FILE *fd, size_t val)
{
char c;
char c1, c2;
if (val >= KRADIX*KRADIX)
{
fprintf(stderr, "error: cannot write %d, max %d\n",
val, KRADIX*KRADIX);
fprintf(stderr, "error: cannot write %ld, max %ld\n",
(long) val, (long) (KRADIX*KRADIX));
exit(1);
}
c = val % KRADIX;
fwrite(&c, sizeof(char), 1, fd);
c = val / KRADIX;
fwrite(&c, sizeof(char), 1, fd);
c1 = (char) (val % KRADIX);
val /= KRADIX;
c2 = (char) (val % KRADIX);
val /= KRADIX;
if (val != 0) {
fprintf(stderr, "error: %ld exceeds max integer size (%ld)\n",
(long) val, (long) (KRADIX*KRADIX));
exit(1);
}
fwrite(&c1, sizeof(char), 1, fd);
fwrite(&c2, sizeof(char), 1, fd);
}
int main(int argc, char *argv[])
int main(int argc, constant char *argv[])
{
struct lesskey_tables tables;
FILE *out;
@ -287,8 +294,8 @@ int main(int argc, char *argv[])
* If there is no HOME environment variable,
* try the concatenation of HOMEDRIVE + HOMEPATH.
*/
char *drive = getenv("HOMEDRIVE");
char *path = getenv("HOMEPATH");
constant char *drive = getenv("HOMEDRIVE");
constant char *path = getenv("HOMEPATH");
if (drive != NULL && path != NULL)
{
char *env = (char *) ecalloc(strlen(drive) +
@ -338,16 +345,16 @@ int main(int argc, char *argv[])
/* Command key section */
fputbytes(out, cmdsection, sizeof(cmdsection));
fputint(out, tables.cmdtable.buf.end);
fputbytes(out, (char *)tables.cmdtable.buf.data, tables.cmdtable.buf.end);
fputbytes(out, xbuf_char_data(&tables.cmdtable.buf), tables.cmdtable.buf.end);
/* Edit key section */
fputbytes(out, editsection, sizeof(editsection));
fputint(out, tables.edittable.buf.end);
fputbytes(out, (char *)tables.edittable.buf.data, tables.edittable.buf.end);
fputbytes(out, xbuf_char_data(&tables.edittable.buf), tables.edittable.buf.end);
/* Environment variable section */
fputbytes(out, varsection, sizeof(varsection));
fputint(out, tables.vartable.buf.end);
fputbytes(out, (char *)tables.vartable.buf.data, tables.vartable.buf.end);
fputbytes(out, xbuf_char_data(&tables.vartable.buf), tables.vartable.buf.end);
/* File trailer */
fputbytes(out, endsection, sizeof(endsection));

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -7,6 +7,7 @@
* For more information, see the README file.
*/
#include "lang.h"
#include "xbuf.h"
/*
@ -41,13 +42,13 @@
struct lesskey_cmdname
{
char *cn_name;
constant char *cn_name;
int cn_action;
};
struct lesskey_table
{
struct lesskey_cmdname *names;
constant struct lesskey_cmdname *names;
struct xbuffer buf;
int is_var;
};
@ -60,4 +61,19 @@ struct lesskey_tables
struct lesskey_table vartable;
};
extern int parse_lesskey(char *infile, struct lesskey_tables *tables);
extern int parse_lesskey(constant char *infile, struct lesskey_tables *tables);
extern int parse_lesskey_content(constant char *content, struct lesskey_tables *tables);
/* keep in sync with less.h */
#if HAVE_SNPRINTF
#define SNPRINTF1(str, size, fmt, v1) snprintf((str), (size), (fmt), (v1))
#define SNPRINTF2(str, size, fmt, v1, v2) snprintf((str), (size), (fmt), (v1), (v2))
#define SNPRINTF3(str, size, fmt, v1, v2, v3) snprintf((str), (size), (fmt), (v1), (v2), (v3))
#define SNPRINTF4(str, size, fmt, v1, v2, v3, v4) snprintf((str), (size), (fmt), (v1), (v2), (v3), (v4))
#else
/* Use unsafe sprintf if we don't have snprintf. */
#define SNPRINTF1(str, size, fmt, v1) sprintf((str), (fmt), (v1))
#define SNPRINTF2(str, size, fmt, v1, v2) sprintf((str), (fmt), (v1), (v2))
#define SNPRINTF3(str, size, fmt, v1, v2, v3) sprintf((str), (fmt), (v1), (v2), (v3))
#define SNPRINTF4(str, size, fmt, v1, v2, v3, v4) sprintf((str), (fmt), (v1), (v2), (v3), (v4))
#endif

View file

@ -1,5 +1,5 @@
'\" t
.TH LESSKEY 1 "Version 643: 20 Jul 2023"
.TH LESSKEY 1 "Version 668: 06 Oct 2024"
.SH NAME
lesskey \- customize key bindings for less
.SH "SYNOPSIS (deprecated)"
@ -122,14 +122,15 @@ For example, see the "{" and ":t" commands in the example below.
The extra string has a special meaning for the "quit" action:
when
.B less
quits, the first character of the extra string is used as its exit status.
quits, the ASCII value of the first character of the extra string
is used as its exit status.
.
.SH EXAMPLE
The following input file describes the set of
default command keys used by
.BR less .
Documentation on each command can be found in the
.less
.B less
man page, under the key sequence which invokes the command.
.sp
.RS 5m
@ -181,8 +182,6 @@ g goto-line
\ee< goto-line
p percent
% percent
\ee[ left-scroll
\ee] right-scroll
\ee( left-scroll
\ee) right-scroll
\ekl left-scroll
@ -213,11 +212,16 @@ n repeat-search
\een repeat-search-all
N reverse-search
\eeN reverse-search-all
^O^N osc8-forw-search
^On osc8-forw-search
^O^P osc8-back-search
^Op osc8-back-search
^O^O osc8-open
& filter
m set-mark
M set-mark-bottom
\eem clear-mark
' goto-mark
\&' goto-mark
^X^X goto-mark
E examine
:e examine
@ -367,6 +371,24 @@ it should be appended to the end of the preceding line.
(It cannot be added to the beginning of the += string because space after
the equals sign is ignored, as noted above.)
.
.sp
In the string after the = sign, a substring of the form ${NAME}
is replaced with the value of the environment variable "NAME".
The value of the variable may come from either the system environment,
an earlier lesskey file, or an earlier definition in the current lesskey file.
Simple text replacements can be performed by using
the syntax ${NAME/STRING/REPL}.
This replaces all instances of "STRING" in the named
environment variable with the text "REPL".
STRING is matched using a simple text comparison;
no metacharacters are supported.
An instance of slash or right curly bracket in STRING or REPL
must be escaped by preceding it with \fItwo\fP backslashes.
If REPL is an empty string, all instances of STRING are removed.
A slash immediately before the right curly bracket may be omitted.
Multiple replacements may be performed by using
the syntax ${NAME/STRING1/REPL1/STRING2/REPL2} and so on.
.
.SH CONDITIONAL CONFIGURATION
If a line begins with #version followed by a relational operator and a version number,
the remainder of the line is parsed if and only if the running version of
@ -410,7 +432,7 @@ In those older versions, all #version lines are ignored.
.
.SH EXAMPLE
The following input file sets the \-i and \-S options when
.less
.B less
is run and, on version 595 and higher, adds a \-\-color option.
.sp
.nf
@ -430,7 +452,7 @@ which start with a NUL character (0).
This NUL character should be represented as \e340 in a lesskey file.
.
.SH COPYRIGHT
Copyright (C) 1984-2023 Mark Nudelman
Copyright (C) 1984-2024 Mark Nudelman
.PP
less is part of the GNU project and is free software.
You can redistribute it and/or modify it

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -20,16 +20,16 @@
extern void lesskey_parse_error(char *msg);
extern char *homefile(char *filename);
extern void *ecalloc(int count, unsigned int size);
extern void *ecalloc(size_t count, size_t size);
extern int lstrtoi(char *str, char **end, int radix);
extern char version[];
static int linenum;
static int errors;
static int less_version = 0;
static char *lesskey_file;
static char *lesskey_file = NULL;
static struct lesskey_cmdname cmdnames[] =
static constant struct lesskey_cmdname cmdnames[] =
{
{ "back-bracket", A_B_BRACKET },
{ "back-line", A_B_LINE },
@ -39,6 +39,7 @@ static struct lesskey_cmdname cmdnames[] =
{ "back-search", A_B_SEARCH },
{ "back-window", A_B_WINDOW },
{ "clear-mark", A_CLRMARK },
{ "clear-search", A_CLR_SEARCH },
{ "debug", A_DEBUG },
{ "digit", A_DIGIT },
{ "display-flag", A_DISP_OPTION },
@ -52,13 +53,13 @@ static struct lesskey_cmdname cmdnames[] =
{ "flush-repaint", A_FREPAINT },
{ "forw-bracket", A_F_BRACKET },
{ "forw-forever", A_F_FOREVER },
{ "forw-until-hilite", A_F_UNTIL_HILITE },
{ "forw-line", A_F_LINE },
{ "forw-line-force", A_FF_LINE },
{ "forw-screen", A_F_SCREEN },
{ "forw-screen-force", A_FF_SCREEN },
{ "forw-scroll", A_F_SCROLL },
{ "forw-search", A_F_SEARCH },
{ "forw-until-hilite", A_F_UNTIL_HILITE },
{ "forw-window", A_F_WINDOW },
{ "goto-end", A_GOEND },
{ "goto-end-buffered", A_GOEND_BUF },
@ -70,12 +71,16 @@ static struct lesskey_cmdname cmdnames[] =
{ "left-scroll", A_LSHIFT },
{ "next-file", A_NEXT_FILE },
{ "next-tag", A_NEXT_TAG },
{ "noaction", A_NOACTION },
{ "no-scroll", A_LLSHIFT },
{ "noaction", A_NOACTION },
{ "osc8-forw-search", A_OSC8_F_SEARCH },
{ "osc8-back-search", A_OSC8_B_SEARCH },
{ "osc8-open", A_OSC8_OPEN },
{ "percent", A_PERCENT },
{ "pipe", A_PIPE },
{ "prev-file", A_PREV_FILE },
{ "prev-tag", A_PREV_TAG },
{ "pshell", A_PSHELL },
{ "quit", A_QUIT },
{ "remove-file", A_REMOVE_FILE },
{ "repaint", A_REPAINT },
@ -88,18 +93,16 @@ static struct lesskey_cmdname cmdnames[] =
{ "set-mark", A_SETMARK },
{ "set-mark-bottom", A_SETMARKBOT },
{ "shell", A_SHELL },
{ "pshell", A_PSHELL },
{ "status", A_STAT },
{ "toggle-flag", A_OPT_TOGGLE },
{ "toggle-option", A_OPT_TOGGLE },
{ "undo-hilite", A_UNDO_SEARCH },
{ "clear-search", A_CLR_SEARCH },
{ "version", A_VERSION },
{ "visual", A_VISUAL },
{ NULL, 0 }
};
static struct lesskey_cmdname editnames[] =
static constant struct lesskey_cmdname editnames[] =
{
{ "back-complete", EC_B_COMPLETE },
{ "backspace", EC_BACKSPACE },
@ -127,12 +130,16 @@ static struct lesskey_cmdname editnames[] =
/*
* Print a parse error message.
*/
static void parse_error(char *fmt, char *arg1)
static void parse_error(constant char *fmt, constant char *arg1)
{
char buf[1024];
int n = snprintf(buf, sizeof(buf), "%s: line %d: ", lesskey_file, linenum);
if (n >= 0 && n < sizeof(buf))
snprintf(buf+n, sizeof(buf)-n, fmt, arg1);
int n = SNPRINTF2(buf, sizeof(buf), "%s: line %d: ", lesskey_file, linenum);
if (n >= 0)
{
size_t len = (size_t) n;
if (len < sizeof(buf))
SNPRINTF1(buf+len, sizeof(buf)-len, fmt, arg1);
}
++errors;
lesskey_parse_error(buf);
}
@ -159,7 +166,7 @@ static void init_tables(struct lesskey_tables *tables)
#define CHAR_STRING_LEN 8
static char * char_string(char *buf, int ch, int lit)
static constant char * char_string(char *buf, char ch, int lit)
{
if (lit || (ch >= 0x20 && ch < 0x7f))
{
@ -167,7 +174,7 @@ static char * char_string(char *buf, int ch, int lit)
buf[1] = '\0';
} else
{
snprintf(buf, CHAR_STRING_LEN, "\\x%02x", ch);
SNPRINTF1(buf, CHAR_STRING_LEN, "\\x%02x", ch);
}
return buf;
}
@ -185,7 +192,7 @@ static char * increment_pointer(char *p)
/*
* Parse one character of a string.
*/
static char * tstr(char **pp, int xlate)
static constant char * tstr(char **pp, int xlate)
{
char *p;
char ch;
@ -209,7 +216,7 @@ static char * tstr(char **pp, int xlate)
ch = 0;
i = 0;
do
ch = 8*ch + (*p - '0');
ch = (char) (8*ch + (*p - '0'));
while (*++p >= '0' && *p <= '7' && ++i < 3);
*pp = p;
if (xlate && ch == CONTROL('K'))
@ -353,10 +360,10 @@ static void erase_cmd_char(struct lesskey_tables *tables)
/*
* Add a string to the output command table.
*/
static void add_cmd_str(char *s, struct lesskey_tables *tables)
static void add_cmd_str(constant char *s, struct lesskey_tables *tables)
{
for ( ; *s != '\0'; s++)
add_cmd_char(*s, tables);
add_cmd_char((unsigned char) *s, tables);
}
/*
@ -382,7 +389,7 @@ static int match_version(char op, int ver)
* If the version matches, return the part of the line that should be executed.
* Otherwise, return NULL.
*/
static char * version_line(char *s, struct lesskey_tables *tables)
static char * version_line(char *s)
{
char op;
int ver;
@ -445,7 +452,7 @@ static char * control_line(char *s, struct lesskey_tables *tables)
}
if (PREFIX(s, "#version"))
{
return (version_line(s, tables));
return (version_line(s));
}
return (s);
}
@ -475,7 +482,7 @@ static void parse_cmdline(char *p, struct lesskey_tables *tables)
{
char *actname;
int action;
char *s;
constant char *s;
char c;
/*
@ -539,7 +546,7 @@ static void parse_cmdline(char *p, struct lesskey_tables *tables)
*/
static void parse_varline(char *line, struct lesskey_tables *tables)
{
char *s;
constant char *s;
char *p = line;
char *eq;
@ -612,14 +619,14 @@ static void parse_line(char *line, struct lesskey_tables *tables)
/*
* Parse a lesskey source file and store result in tables.
*/
int parse_lesskey(char *infile, struct lesskey_tables *tables)
int parse_lesskey(constant char *infile, struct lesskey_tables *tables)
{
FILE *desc;
char line[1024];
if (infile == NULL)
infile = homefile(DEF_LESSKEYINFILE);
lesskey_file = infile;
lesskey_file = (infile != NULL) ? strdup(infile) : homefile(DEF_LESSKEYINFILE);
if (lesskey_file == NULL)
return (-1);
init_tables(tables);
errors = 0;
@ -630,22 +637,63 @@ int parse_lesskey(char *infile, struct lesskey_tables *tables)
/*
* Open the input file.
*/
if (strcmp(infile, "-") == 0)
if (strcmp(lesskey_file, "-") == 0)
desc = stdin;
else if ((desc = fopen(infile, "r")) == NULL)
else if ((desc = fopen(lesskey_file, "r")) == NULL)
{
/* parse_error("cannot open lesskey file %s", infile); */
return (-1);
/* parse_error("cannot open lesskey file %s", lesskey_file); */
errors = -1;
}
/*
* Read and parse the input file, one line at a time.
*/
while (fgets(line, sizeof(line), desc) != NULL)
if (desc != NULL)
{
++linenum;
parse_line(line, tables);
while (fgets(line, sizeof(line), desc) != NULL)
{
++linenum;
parse_line(line, tables);
}
if (desc != stdin)
fclose(desc);
}
fclose(desc);
free(lesskey_file);
lesskey_file = NULL;
return (errors);
}
/*
* Parse a lesskey source content and store result in tables.
*/
int parse_lesskey_content(constant char *content, struct lesskey_tables *tables)
{
size_t cx = 0;
lesskey_file = "lesskey-content";
init_tables(tables);
errors = 0;
linenum = 0;
if (less_version == 0)
less_version = lstrtoi(version, NULL, 10);
while (content[cx] != '\0')
{
/* Extract a line from the content buffer and parse it. */
char line[1024];
size_t lx = 0;
while (content[cx] != '\0' && content[cx] != '\n' && content[cx] != ';')
{
if (lx >= sizeof(line)-1) break;
if (content[cx] == '\\' && content[cx+1] == ';')
++cx; /* escaped semicolon: skip the backslash */
line[lx++] = content[cx++];
}
line[lx] = '\0';
++linenum;
parse_line(line, tables);
if (content[cx] != '\0') ++cx; /* skip newline or semicolon */
}
lesskey_file = NULL;
return (errors);
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -25,12 +25,12 @@
#define MAX_PFX_WIDTH (MAX_LINENUM_WIDTH + MAX_STATUSCOL_WIDTH + 1)
static struct {
char *buf; /* Buffer which holds the current output line */
int *attr; /* Parallel to buf, to hold attributes */
int print; /* Index in buf of first printable char */
int end; /* Number of chars in buf */
int *attr; /* Parallel to buf, to hold attributes */
size_t print; /* Index in buf of first printable char */
size_t end; /* Number of chars in buf */
char pfx[MAX_PFX_WIDTH]; /* Holds status column and line number */
int pfx_attr[MAX_PFX_WIDTH];
int pfx_end; /* Number of chars in pfx */
size_t pfx_end; /* Number of chars in pfx */
} linebuf;
/*
@ -50,9 +50,9 @@ static struct xbuffer last_ansi;
static struct xbuffer last_ansis[NUM_LAST_ANSIS];
static int curr_last_ansi;
public int size_linebuf = 0; /* Size of line buffer (and attr buffer) */
public size_t size_linebuf = 0; /* Size of line buffer (and attr buffer) */
static struct ansi_state *line_ansi = NULL;
static int ansi_in_line;
static lbool ansi_in_line;
static int hlink_in_line;
static int line_mark_attr;
static int cshift; /* Current left-shift of output line buffer */
@ -68,16 +68,16 @@ static int right_curr;
static int right_column;
static int overstrike; /* Next char should overstrike previous char */
static int last_overstrike = AT_NORMAL;
static int is_null_line; /* There is no current line */
static lbool is_null_line; /* There is no current line */
static LWCHAR pendc;
static POSITION pendpos;
static char *end_ansi_chars;
static char *mid_ansi_chars;
static constant char *end_ansi_chars;
static constant char *mid_ansi_chars;
static int in_hilite;
static int attr_swidth(int a);
static int attr_ewidth(int a);
static int do_append(LWCHAR ch, char *rep, POSITION pos);
static int do_append(LWCHAR ch, constant char *rep, POSITION pos);
extern int sigs;
extern int bs_mode;
@ -87,7 +87,6 @@ extern int proc_return;
extern int linenums;
extern int ctldisp;
extern int twiddle;
extern int binattr;
extern int status_col;
extern int status_col_width;
extern int linenum_width;
@ -100,7 +99,7 @@ extern int sc_width, sc_height;
extern int utf_mode;
extern POSITION start_attnpos;
extern POSITION end_attnpos;
extern char rscroll_char;
extern LWCHAR rscroll_char;
extern int rscroll_attr;
extern int use_color;
extern int status_line;
@ -109,7 +108,7 @@ static char mbc_buf[MAX_UTF_CHAR_LEN];
static int mbc_buf_len = 0;
static int mbc_buf_index = 0;
static POSITION mbc_pos;
static int saved_line_end;
static size_t saved_line_end;
static int saved_end_column;
/* Configurable color map */
@ -138,9 +137,8 @@ static struct color_map color_map[] = {
/* State while processing an ANSI escape sequence */
struct ansi_state {
int hindex; /* Index into hyperlink prefix */
int hlink; /* Processing hyperlink address? */
int prev_esc; /* Prev char was ESC (to detect ESC-\ seq) */
int oindex; /* Index into OSC8 prefix */
osc8_state ostate; /* State while processing OSC8 sequence */
};
/*
@ -174,7 +172,7 @@ public void init_line(void)
static int expand_linebuf(void)
{
/* Double the size of the line buffer. */
int new_size = size_linebuf * 2;
size_t new_size = size_linebuf * 2;
char *new_buf = (char *) calloc(new_size, sizeof(char));
int *new_attr = (int *) calloc(new_size, sizeof(int));
if (new_buf == NULL || new_attr == NULL)
@ -201,7 +199,7 @@ static int expand_linebuf(void)
/*
* Is a character ASCII?
*/
public int is_ascii_char(LWCHAR ch)
public lbool is_ascii_char(LWCHAR ch)
{
return (ch <= 0x7F);
}
@ -213,7 +211,7 @@ static void inc_end_column(int w)
if (end_column > right_column && w > 0)
{
right_column = end_column;
right_curr = linebuf.end;
right_curr = (int) linebuf.end;
}
end_column += w;
}
@ -245,10 +243,10 @@ public void prewind(void)
overstrike = 0;
last_overstrike = AT_NORMAL;
mbc_buf_len = 0;
is_null_line = 0;
is_null_line = FALSE;
pendc = '\0';
in_hilite = 0;
ansi_in_line = 0;
ansi_in_line = FALSE;
hlink_in_line = 0;
line_mark_attr = 0;
line_pos = NULL_POSITION;
@ -262,7 +260,7 @@ public void prewind(void)
/*
* Set a character in the line buffer.
*/
static void set_linebuf(int n, char ch, int attr)
static void set_linebuf(size_t n, char ch, int attr)
{
if (n >= size_linebuf)
{
@ -289,7 +287,7 @@ static void add_linebuf(char ch, int attr, int w)
/*
* Append a string to the line buffer.
*/
static void addstr_linebuf(char *s, int attr, int cw)
static void addstr_linebuf(constant char *s, int attr, int cw)
{
for ( ; *s != '\0'; s++)
add_linebuf(*s, attr, cw);
@ -298,7 +296,7 @@ static void addstr_linebuf(char *s, int attr, int cw)
/*
* Set a character in the line prefix buffer.
*/
static void set_pfx(int n, char ch, int attr)
static void set_pfx(size_t n, char ch, int attr)
{
linebuf.pfx[n] = ch;
linebuf.pfx_attr[n] = attr;
@ -318,7 +316,6 @@ static void add_pfx(char ch, int attr)
public void plinestart(POSITION pos)
{
LINENUM linenum = 0;
int i;
if (linenums == OPT_ONPLUS)
{
@ -347,7 +344,7 @@ public void plinestart(POSITION pos)
if (status_col)
{
add_pfx(c ? c : ' ', line_mark_attr); /* column 0: status */
while (linebuf.pfx_end < status_col_width)
while (linebuf.pfx_end < (size_t) status_col_width) /*{{type-issue}}*/
add_pfx(' ', AT_NORMAL);
}
}
@ -359,7 +356,8 @@ public void plinestart(POSITION pos)
if (linenums == OPT_ONPLUS)
{
char buf[INT_STRLEN_BOUND(linenum) + 2];
int len;
size_t len;
size_t i;
linenum = vlinenum(linenum);
if (linenum == 0)
@ -367,15 +365,15 @@ public void plinestart(POSITION pos)
else
{
linenumtoa(linenum, buf, 10);
len = (int) strlen(buf);
len = strlen(buf);
}
for (i = 0; i < linenum_width - len; i++)
for (i = 0; i + len < (size_t) linenum_width; i++)
add_pfx(' ', AT_NORMAL);
for (i = 0; i < len; i++)
add_pfx(buf[i], AT_BOLD|AT_COLOR_LINENUM);
add_pfx(' ', AT_NORMAL);
}
end_column = linebuf.pfx_end;
end_column = (int) linebuf.pfx_end; /*{{type-issue}}*/
}
/*
@ -398,12 +396,13 @@ public int line_pfx_width(void)
*/
public void pshift_all(void)
{
int i;
size_t i;
for (i = linebuf.print; i < linebuf.end; i++)
if (linebuf.attr[i] == AT_ANSI)
xbuf_add_byte(&shifted_ansi, (unsigned char) linebuf.buf[i]);
xbuf_add_char(&shifted_ansi, linebuf.buf[i]);
linebuf.end = linebuf.print;
end_column = linebuf.pfx_end;
end_column = (int) linebuf.pfx_end; /*{{type-issue}}*/
line_pos = NULL_POSITION;
}
/*
@ -466,13 +465,13 @@ public int pwidth(LWCHAR ch, int a, LWCHAR prev_ch, int prev_a)
* Backspace moves backwards one or two positions.
*/
if (prev_a & (AT_ANSI|AT_BINARY))
return strlen(prchar('\b'));
return (int) strlen(prchar('\b')); /*{{type-issue}}*/
return (utf_mode && is_wide_char(prev_ch)) ? -2 : -1;
}
if (!utf_mode || is_ascii_char(ch))
{
if (control_char((char)ch))
if (control_char(ch))
{
/*
* Control characters do unpredictable things,
@ -532,7 +531,7 @@ static int backc(void)
{
LWCHAR prev_ch;
int width;
linebuf.end = (int) (p - linebuf.buf);
linebuf.end = ptr_diff(p, linebuf.buf);
prev_ch = step_char(&p, -1, linebuf.buf);
width = pwidth(ch, linebuf.attr[linebuf.end], prev_ch, linebuf.attr[linebuf.end-1]);
end_column -= width;
@ -565,22 +564,22 @@ public void loadc(void)
/*
* Is a character the end of an ANSI escape sequence?
*/
public int is_ansi_end(LWCHAR ch)
public lbool is_ansi_end(LWCHAR ch)
{
if (!is_ascii_char(ch))
return (0);
return (FALSE);
return (strchr(end_ansi_chars, (char) ch) != NULL);
}
/*
* Can a char appear in an ANSI escape sequence, before the end char?
*/
public int is_ansi_middle(LWCHAR ch)
public lbool is_ansi_middle(LWCHAR ch)
{
if (!is_ascii_char(ch))
return (0);
return (FALSE);
if (is_ansi_end(ch))
return (0);
return (FALSE);
return (strchr(mid_ansi_chars, (char) ch) != NULL);
}
@ -588,11 +587,11 @@ public int is_ansi_middle(LWCHAR ch)
* Skip past an ANSI escape sequence.
* pp is initially positioned just after the CSI_START char.
*/
public void skip_ansi(struct ansi_state *pansi, char **pp, constant char *limit)
public void skip_ansi(struct ansi_state *pansi, constant char **pp, constant char *limit)
{
LWCHAR c;
do {
c = step_char(pp, +1, limit);
c = step_charc(pp, +1, limit);
} while (*pp < limit && ansi_step(pansi, c) == ANSI_MID);
/* Note that we discard final char, for which is_ansi_end is true. */
}
@ -608,9 +607,8 @@ public struct ansi_state * ansi_start(LWCHAR ch)
if (!IS_CSI_START(ch))
return NULL;
pansi = ecalloc(1, sizeof(struct ansi_state));
pansi->hindex = 0;
pansi->hlink = 0;
pansi->prev_esc = 0;
pansi->oindex = 0;
pansi->ostate = OSC8_PREFIX;
return pansi;
}
@ -618,30 +616,48 @@ public struct ansi_state * ansi_start(LWCHAR ch)
* Determine whether the next char in an ANSI escape sequence
* ends the sequence.
*/
public int ansi_step(struct ansi_state *pansi, LWCHAR ch)
public ansi_state ansi_step(struct ansi_state *pansi, LWCHAR ch)
{
if (pansi->hlink)
static constant char osc8_prefix[] = ESCS "]8;";
switch (pansi->ostate)
{
/* Hyperlink ends with \7 or ESC-backslash. */
if (ch == '\7')
return ANSI_END;
if (pansi->prev_esc)
return (ch == '\\') ? ANSI_END : ANSI_ERR;
pansi->prev_esc = (ch == ESC);
return ANSI_MID;
}
if (pansi->hindex >= 0)
{
static char hlink_prefix[] = ESCS "]8;";
if (ch == hlink_prefix[pansi->hindex] ||
(pansi->hindex == 0 && IS_CSI_START(ch)))
case OSC8_PREFIX:
if (ch != (LWCHAR) osc8_prefix[pansi->oindex] &&
!(pansi->oindex == 0 && IS_CSI_START(ch)))
{
pansi->hindex++;
if (hlink_prefix[pansi->hindex] == '\0')
pansi->hlink = 1; /* now processing hyperlink addr */
return ANSI_MID;
pansi->ostate = OSC8_NOT; /* not an OSC8 sequence */
break;
}
pansi->hindex = -1; /* not a hyperlink */
pansi->oindex++;
if (osc8_prefix[pansi->oindex] == '\0') /* end of prefix */
pansi->ostate = OSC8_PARAMS;
return ANSI_MID;
case OSC8_PARAMS:
if (ch == ';')
pansi->ostate = OSC8_URI;
return ANSI_MID;
case OSC8_URI:
/* URI ends with \7 or ESC-backslash. */
if (ch == '\7')
{
pansi->ostate = OSC8_END;
return ANSI_END;
}
if (ch == ESC)
pansi->ostate = OSC8_ST_ESC;
return ANSI_MID;
case OSC8_ST_ESC:
if (ch != '\\')
{
return ANSI_ERR;
}
pansi->ostate = OSC8_END;
return ANSI_END;
case OSC8_END:
return ANSI_END;
case OSC8_NOT:
break;
}
/* Check for SGR sequences */
if (is_ansi_middle(ch))
@ -651,6 +667,14 @@ public int ansi_step(struct ansi_state *pansi, LWCHAR ch)
return ANSI_ERR;
}
/*
* Return the current OSC8 parsing state.
*/
public osc8_state ansi_osc8_state(struct ansi_state *pansi)
{
return pansi->ostate;
}
/*
* Free an ansi_state structure.
*/
@ -678,16 +702,17 @@ static int fits_on_screen(int w, int a)
if (store_char((ch),(a),(rep),(pos))) return (1); \
} while (0)
static int store_char(LWCHAR ch, int a, char *rep, POSITION pos)
static int store_char(LWCHAR ch, int a, constant char *rep, POSITION pos)
{
int w;
int i;
int replen;
size_t i;
size_t replen;
char cs;
int ov;
i = (a & (AT_UNDERLINE|AT_BOLD));
if (i != AT_NORMAL)
last_overstrike = i;
ov = (a & (AT_UNDERLINE|AT_BOLD));
if (ov != AT_NORMAL)
last_overstrike = ov;
#if HILITE_SEARCH
{
@ -759,7 +784,7 @@ static int store_char(LWCHAR ch, int a, char *rep, POSITION pos)
replen = 1;
} else
{
replen = utf_len(rep[0]);
replen = (size_t) utf_len(rep[0]); /*{{type-issue}}*/
}
if (cshift == hshift)
@ -770,7 +795,7 @@ static int store_char(LWCHAR ch, int a, char *rep, POSITION pos)
{
/* Copy shifted ANSI sequences to beginning of line. */
for (i = 0; i < shifted_ansi.end; i++)
add_linebuf(shifted_ansi.data[i], AT_ANSI, 0);
add_linebuf((char) shifted_ansi.data[i], AT_ANSI, 0);
xbuf_reset(&shifted_ansi);
}
}
@ -784,11 +809,11 @@ static int store_char(LWCHAR ch, int a, char *rep, POSITION pos)
{
/* We haven't left-shifted enough yet. */
if (a == AT_ANSI)
xbuf_add_byte(&shifted_ansi, (unsigned char) ch); /* Save ANSI attributes */
xbuf_add_char(&shifted_ansi, (char) ch); /* Save ANSI attributes */
if (linebuf.end > linebuf.print)
{
/* Shift left enough to put last byte of this char at print-1. */
int i;
size_t i;
for (i = 0; i < linebuf.print; i++)
{
linebuf.buf[i] = linebuf.buf[i+replen];
@ -814,25 +839,21 @@ static int store_char(LWCHAR ch, int a, char *rep, POSITION pos)
#define STORE_STRING(s,a,pos) \
do { if (store_string((s),(a),(pos))) return (1); } while (0)
static int store_string(char *s, int a, POSITION pos)
static int store_string(constant char *s, int a, POSITION pos)
{
if (!fits_on_screen(strlen(s), a))
if (!fits_on_screen((int) strlen(s), a))
return 1;
for ( ; *s != 0; s++)
STORE_CHAR(*s, a, NULL, pos);
STORE_CHAR((LWCHAR)*s, a, NULL, pos);
return 0;
}
/*
* Append a tab to the line buffer.
* Store spaces to represent the tab.
* Return number of spaces from col to the next tab stop.
*/
#define STORE_TAB(a,pos) \
do { if (store_tab((a),(pos))) return (1); } while (0)
static int store_tab(int attr, POSITION pos)
static int tab_spaces(int col)
{
int to_tab = end_column - linebuf.pfx_end;
int to_tab = col - (int) linebuf.pfx_end; /*{{type-issue}}*/
if (ntabstops < 2 || to_tab >= tabstops[ntabstops-1])
to_tab = tabdefault -
@ -845,7 +866,19 @@ static int store_tab(int attr, POSITION pos)
break;
to_tab = tabstops[i+1] - to_tab;
}
return to_tab;
}
/*
* Append a tab to the line buffer.
* Store spaces to represent the tab.
*/
#define STORE_TAB(a,pos) \
do { if (store_tab((a),(pos))) return (1); } while (0)
static int store_tab(int attr, POSITION pos)
{
int to_tab = tab_spaces(end_column);
do {
STORE_CHAR(' ', attr, " ", pos);
} while (--to_tab > 0);
@ -869,7 +902,7 @@ static int flush_mbc_buf(POSITION pos)
int i;
for (i = 0; i < mbc_buf_index; i++)
if (store_prchar(mbc_buf[i], pos))
if (store_prchar((LWCHAR) mbc_buf[i], pos))
return mbc_buf_index - i;
return 0;
}
@ -879,13 +912,14 @@ static int flush_mbc_buf(POSITION pos)
* Expand tabs into spaces, handle underlining, boldfacing, etc.
* Returns 0 if ok, 1 if couldn't fit in buffer.
*/
public int pappend(int c, POSITION pos)
public int pappend_b(char c, POSITION pos, lbool before_pendc)
{
LWCHAR ch = c & 0377;
int r;
if (pendc)
if (pendc && !before_pendc)
{
if (c == '\r' && pendc == '\r')
if (ch == '\r' && pendc == '\r')
return (0);
if (do_append(pendc, NULL, pendpos))
/*
@ -896,7 +930,7 @@ public int pappend(int c, POSITION pos)
pendc = '\0';
}
if (c == '\r' && (proc_return == OPT_ON || (bs_mode == BS_SPECIAL && proc_return == OPT_OFF)))
if (ch == '\r' && (proc_return == OPT_ON || (bs_mode == BS_SPECIAL && proc_return == OPT_OFF)))
{
if (mbc_buf_len > 0) /* utf_mode must be on. */
{
@ -913,14 +947,14 @@ public int pappend(int c, POSITION pos)
* the next char. If the next char is a newline,
* discard the CR.
*/
pendc = c;
pendc = ch;
pendpos = pos;
return (0);
}
if (!utf_mode)
{
r = do_append(c, NULL, pos);
r = do_append(ch, NULL, pos);
} else
{
/* Perform strict validation in all possible cases. */
@ -930,7 +964,7 @@ public int pappend(int c, POSITION pos)
mbc_buf_index = 1;
*mbc_buf = c;
if (IS_ASCII_OCTET(c))
r = do_append(c, NULL, pos);
r = do_append(ch, NULL, pos);
else if (IS_UTF8_LEAD(c))
{
mbc_buf_len = utf_len(c);
@ -969,7 +1003,12 @@ public int pappend(int c, POSITION pos)
return (r);
}
static int store_control_char(LWCHAR ch, char *rep, POSITION pos)
public int pappend(char c, POSITION pos)
{
return pappend_b(c, pos, FALSE);
}
static int store_control_char(LWCHAR ch, constant char *rep, POSITION pos)
{
if (ctldisp == OPT_ON)
{
@ -978,26 +1017,26 @@ static int store_control_char(LWCHAR ch, char *rep, POSITION pos)
} else
{
/* Output a printable representation of the character. */
STORE_PRCHAR((char) ch, pos);
STORE_PRCHAR(ch, pos);
}
return (0);
}
static int store_ansi(LWCHAR ch, char *rep, POSITION pos)
static int store_ansi(LWCHAR ch, constant char *rep, POSITION pos)
{
switch (ansi_step(line_ansi, ch))
{
case ANSI_MID:
STORE_CHAR(ch, AT_ANSI, rep, pos);
if (line_ansi->hlink)
if (ansi_osc8_state(line_ansi) == OSC8_PARAMS)
hlink_in_line = 1;
xbuf_add_byte(&last_ansi, (unsigned char) ch);
xbuf_add_char(&last_ansi, (char) ch);
break;
case ANSI_END:
STORE_CHAR(ch, AT_ANSI, rep, pos);
ansi_done(line_ansi);
line_ansi = NULL;
xbuf_add_byte(&last_ansi, (unsigned char) ch);
xbuf_add_char(&last_ansi, (char) ch);
xbuf_set(&last_ansis[curr_last_ansi], &last_ansi);
xbuf_reset(&last_ansi);
curr_last_ansi = (curr_last_ansi + 1) % NUM_LAST_ANSIS;
@ -1005,24 +1044,26 @@ static int store_ansi(LWCHAR ch, char *rep, POSITION pos)
case ANSI_ERR:
{
/* Remove whole unrecognized sequence. */
char *start = (cshift < hshift) ? xbuf_char_data(&shifted_ansi): linebuf.buf;
int *end = (cshift < hshift) ? &shifted_ansi.end : &linebuf.end;
char *p = start + *end;
constant char *start = (cshift < hshift) ? xbuf_char_data(&shifted_ansi): linebuf.buf;
size_t *end = (cshift < hshift) ? &shifted_ansi.end : &linebuf.end;
constant char *p = start + *end;
LWCHAR bch;
do {
bch = step_char(&p, -1, start);
bch = step_charc(&p, -1, start);
} while (p > start && !IS_CSI_START(bch));
*end = (int) (p - start);
*end = ptr_diff(p, start);
}
xbuf_reset(&last_ansi);
ansi_done(line_ansi);
line_ansi = NULL;
break;
default:
break;
}
return (0);
}
static int store_bs(LWCHAR ch, char *rep, POSITION pos)
static int store_bs(LWCHAR ch, constant char *rep, POSITION pos)
{
if (proc_backspace == OPT_ONPLUS || (bs_mode == BS_CONTROL && proc_backspace == OPT_OFF))
return store_control_char(ch, rep, pos);
@ -1037,7 +1078,7 @@ static int store_bs(LWCHAR ch, char *rep, POSITION pos)
return 0;
}
static int do_append(LWCHAR ch, char *rep, POSITION pos)
static int do_append(LWCHAR ch, constant char *rep, POSITION pos)
{
int a = AT_NORMAL;
int in_overstrike = overstrike;
@ -1046,7 +1087,7 @@ static int do_append(LWCHAR ch, char *rep, POSITION pos)
{
line_ansi = ansi_start(ch);
if (line_ansi != NULL)
ansi_in_line = 1;
ansi_in_line = TRUE;
}
overstrike = 0;
@ -1125,7 +1166,7 @@ static int do_append(LWCHAR ch, char *rep, POSITION pos)
STORE_TAB(a, pos);
return (0);
}
if ((!utf_mode || is_ascii_char(ch)) && control_char((char)ch))
if ((!utf_mode || is_ascii_char(ch)) && control_char(ch))
{
return store_control_char(ch, rep, pos);
} else if (utf_mode && ctldisp != OPT_ON && is_ubin_char(ch))
@ -1183,6 +1224,9 @@ public void pdone(int endline, int chopped, int forw)
if (chopped && rscroll_char)
{
char rscroll_utf8[MAX_UTF_CHAR_LEN+1];
char *up = rscroll_utf8;
/*
* Display the right scrolling char.
* If we've already filled the rightmost screen char
@ -1192,7 +1236,7 @@ public void pdone(int endline, int chopped, int forw)
{
/* We've already written in the rightmost char. */
end_column = right_column;
linebuf.end = right_curr;
linebuf.end = (size_t) right_curr;
}
add_attr_normal();
while (end_column < sc_width-1 + cshift)
@ -1202,10 +1246,13 @@ public void pdone(int endline, int chopped, int forw)
* This may be necessary if the char we overwrote
* was double-width.
*/
add_linebuf(' ', rscroll_attr, 1);
add_linebuf(' ', 0, 1);
}
/* Print rscroll char. It must be single-width. */
add_linebuf(rscroll_char, rscroll_attr, 1);
/* Print rscroll char. */
put_wchar(&up, rscroll_char);
*up = '\0';
addstr_linebuf(rscroll_utf8, rscroll_attr, 0);
inc_end_column(1); /* assume rscroll_char is single-width */
} else
{
add_attr_normal();
@ -1258,12 +1305,121 @@ public void pdone(int endline, int chopped, int forw)
set_linebuf(linebuf.end, '\0', AT_NORMAL);
}
/*
* Return the column number (screen position) of a given file position in its line.
* linepos = position of first char in line
* spos = position of char being queried
* saved_pos = position of a known column, or NULL_POSITION if no known column
* saved_col = column number of a known column, or -1 if no known column
*
* This attempts to mimic the logic in pappend() and the store_*() functions.
* Duplicating this complicated logic is not a good design.
*/
struct col_pos { int col; POSITION pos; };
static void col_vs_pos(POSITION linepos, mutable struct col_pos *cp, POSITION saved_pos, int saved_col)
{
int col = (saved_col < 0) ? 0 : saved_col;
LWCHAR prev_ch = 0;
struct ansi_state *pansi = NULL;
char utf8_buf[MAX_UTF_CHAR_LEN];
int utf8_len = 0;
POSITION chpos;
if (ch_seek(saved_pos != NULL_POSITION ? saved_pos : linepos))
return;
for (;;)
{
int ich;
char ch;
int cw = 0;
chpos = ch_tell();
ich = ch_forw_get();
ch = (char) ich;
if (ich == EOI || ch == '\n')
break;
if (pansi != NULL)
{
if (ansi_step(pansi, ch) != ANSI_MID)
{
ansi_done(pansi);
pansi = NULL;
}
} else if (ctldisp == OPT_ONPLUS && (pansi = ansi_start(ch)) != NULL)
{
/* start of ansi sequence */
(void) ansi_step(pansi, ch);
} else if (ch == '\b')
{
if (proc_backspace == OPT_ONPLUS || (bs_mode == BS_CONTROL && proc_backspace == OPT_OFF))
cw = strlen(prchar(ch));
else
cw = (utf_mode && is_wide_char(prev_ch)) ? -2 : -1;
} else if (ch == '\t')
{
if (proc_tab == OPT_ONPLUS || (bs_mode == BS_CONTROL && proc_tab == OPT_OFF))
cw = strlen(prchar(ch));
else
cw = tab_spaces(col);
} else if ((!utf_mode || is_ascii_char(ch)) && control_char(ch))
{
cw = strlen(prchar(ch));
} else if (utf8_len < MAX_UTF_CHAR_LEN)
{
utf8_buf[utf8_len++] = ch;
if (is_utf8_well_formed(utf8_buf, utf8_len))
{
LWCHAR wch = get_wchar(utf8_buf);
utf8_len = 0;
int attr = 0; /* {{ ignoring attribute is not correct for magic cookie terminals }} */
if (utf_mode && ctldisp != OPT_ON && is_ubin_char(wch))
cw = strlen(prutfchar(wch));
else
cw = pwidth(wch, attr, prev_ch, attr);
prev_ch = wch;
}
} else
{
utf8_len = 0; /* flush invalid UTF-8 */
}
if (cp->pos != NULL_POSITION && chpos == cp->pos) /* found the position we want */
break;
if (cp->col >= 0 && col >= cp->col && cw > 0) /* found the column we want */
break;
col += cw;
prev_ch = ch;
}
cp->col = col;
cp->pos = chpos;
}
public int col_from_pos(POSITION linepos, POSITION spos, POSITION saved_pos, int saved_col)
{
struct col_pos cp;
cp.pos = spos;
cp.col = -1;
col_vs_pos(linepos, &cp, saved_pos, saved_col);
return cp.col;
}
public POSITION pos_from_col(POSITION linepos, int col, POSITION saved_pos, int saved_col)
{
struct col_pos cp;
cp.col = col + hshift - line_pfx_width();
cp.pos = NULL_POSITION;
col_vs_pos(linepos, &cp, saved_pos, saved_col);
return cp.pos;
}
/*
* Set an attribute on each char of the line in the line buffer.
*/
public void set_attr_line(int a)
{
int i;
size_t i;
for (i = linebuf.print; i < linebuf.end; i++)
if ((linebuf.attr[i] & AT_COLOR) == 0 || (a & AT_COLOR) == 0)
@ -1283,7 +1439,7 @@ public void set_status_col(char c, int attr)
* Return the character as the function return value,
* and the character attribute in *ap.
*/
public int gline(int i, int *ap)
public int gline(size_t i, int *ap)
{
if (is_null_line)
{
@ -1320,7 +1476,7 @@ public int gline(int i, int *ap)
*/
public void null_line(void)
{
is_null_line = 1;
is_null_line = TRUE;
cshift = 0;
}
@ -1329,9 +1485,9 @@ public void null_line(void)
* lines which are not split for screen width.
* {{ This is supposed to be more efficient than forw_line(). }}
*/
public POSITION forw_raw_line(POSITION curr_pos, char **linep, int *line_lenp)
public POSITION forw_raw_line_len(POSITION curr_pos, size_t read_len, constant char **linep, size_t *line_lenp)
{
int n;
size_t n;
int c;
POSITION new_pos;
@ -1359,7 +1515,12 @@ public POSITION forw_raw_line(POSITION curr_pos, char **linep, int *line_lenp)
break;
}
}
linebuf.buf[n++] = c;
linebuf.buf[n++] = (char) c;
if (read_len != size_t_null && read_len > 0 && n >= read_len)
{
new_pos = ch_tell();
break;
}
c = ch_forw_get();
}
linebuf.buf[n] = '\0';
@ -1370,13 +1531,18 @@ public POSITION forw_raw_line(POSITION curr_pos, char **linep, int *line_lenp)
return (new_pos);
}
public POSITION forw_raw_line(POSITION curr_pos, constant char **linep, size_t *line_lenp)
{
return forw_raw_line_len(curr_pos, size_t_null, linep, line_lenp);
}
/*
* Analogous to back_line(), but deals with "raw lines".
* {{ This is supposed to be more efficient than back_line(). }}
*/
public POSITION back_raw_line(POSITION curr_pos, char **linep, int *line_lenp)
public POSITION back_raw_line(POSITION curr_pos, constant char **linep, size_t *line_lenp)
{
int n;
size_t n;
int c;
POSITION new_pos;
@ -1410,7 +1576,7 @@ public POSITION back_raw_line(POSITION curr_pos, char **linep, int *line_lenp)
}
if (n <= 0)
{
int old_size_linebuf = size_linebuf;
size_t old_size_linebuf = size_linebuf;
char *fm;
char *to;
if (expand_linebuf())
@ -1431,7 +1597,7 @@ public POSITION back_raw_line(POSITION curr_pos, char **linep, int *line_lenp)
*to = *fm;
n = size_linebuf - old_size_linebuf;
}
linebuf.buf[--n] = c;
linebuf.buf[--n] = (char) c;
}
if (linep != NULL)
*linep = &linebuf.buf[n];
@ -1444,16 +1610,16 @@ public POSITION back_raw_line(POSITION curr_pos, char **linep, int *line_lenp)
* Skip cols printable columns at the start of line.
* Return number of bytes skipped.
*/
public int skip_columns(int cols, char **linep, int *line_lenp)
public int skip_columns(int cols, constant char **linep, size_t *line_lenp)
{
char *line = *linep;
char *eline = line + *line_lenp;
constant char *line = *linep;
constant char *eline = line + *line_lenp;
LWCHAR pch = 0;
int bytes;
size_t bytes;
while (cols > 0 && line < eline)
{
LWCHAR ch = step_char(&line, +1, eline);
LWCHAR ch = step_charc(&line, +1, eline);
struct ansi_state *pansi = ansi_start(ch);
if (pansi != NULL)
{
@ -1467,10 +1633,10 @@ public int skip_columns(int cols, char **linep, int *line_lenp)
pch = ch;
}
}
bytes = line - *linep;
bytes = ptr_diff(line, *linep);
*linep = line;
*line_lenp -= bytes;
return (bytes);
return (int) bytes; /*{{type-issue}}*/
}
/*
@ -1520,13 +1686,15 @@ public int rrshift(void)
{
POSITION pos;
int save_width;
int line;
int sline;
int longest = 0;
save_width = sc_width;
sc_width = INT_MAX;
pos = position(TOP);
for (line = 0; line < sc_height && pos != NULL_POSITION; line++)
sc_width = INT_MAX; /* so forw_line() won't chop */
for (sline = TOP; sline < sc_height; sline++)
if ((pos = position(sline)) != NULL_POSITION)
break;
for (; sline < sc_height && pos != NULL_POSITION; sline++)
{
pos = forw_line(pos);
if (end_column > longest)
@ -1544,7 +1712,7 @@ public int rrshift(void)
static int lookup_color_index(int attr)
{
int cx;
for (cx = 0; cx < sizeof(color_map)/sizeof(*color_map); cx++)
for (cx = 0; cx < countof(color_map); cx++)
if (color_map[cx].attr == attr)
return cx;
return -1;
@ -1568,14 +1736,14 @@ static int color_index(int attr)
/*
* Set the color string to use for a given attribute.
*/
public int set_color_map(int attr, char *colorstr)
public int set_color_map(int attr, constant char *colorstr)
{
int cx = color_index(attr);
if (cx < 0)
return -1;
if (strlen(colorstr)+1 > sizeof(color_map[cx].color))
return -1;
if (*colorstr != '\0' && parse_color(colorstr, NULL, NULL) == CT_NULL)
if (*colorstr != '\0' && parse_color(colorstr, NULL, NULL, NULL) == CT_NULL)
return -1;
strcpy(color_map[cx].color, colorstr);
return 0;
@ -1584,7 +1752,7 @@ public int set_color_map(int attr, char *colorstr)
/*
* Get the color string to use for a given attribute.
*/
public char * get_color_map(int attr)
public constant char * get_color_map(int attr)
{
int cx = color_index(attr);
if (cx < 0)

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -63,12 +63,11 @@ static struct linenum_info anchor; /* Anchor of the list */
static struct linenum_info *freelist; /* Anchor of the unused entries */
static struct linenum_info pool[NPOOL]; /* The pool itself */
static struct linenum_info *spare; /* We always keep one spare entry */
public int scanning_eof = FALSE;
public lbool scanning_eof = FALSE;
extern int linenums;
extern int sigs;
extern int sc_height;
extern int screen_trashed;
extern int header_lines;
extern int nonum_headers;
@ -210,28 +209,41 @@ static void longloopmessage(void)
ierror("Calculating line numbers", NULL_PARG);
}
static int loopcount;
struct delayed_msg
{
void (*message)(void);
int loopcount;
#if HAVE_TIME
static time_type startime;
time_type startime;
#endif
};
static void longish(void)
static void start_delayed_msg(struct delayed_msg *dmsg, void (*message)(void))
{
dmsg->loopcount = 0;
dmsg->message = message;
#if HAVE_TIME
dmsg->startime = get_time();
#endif
}
static void delayed_msg(struct delayed_msg *dmsg)
{
#if HAVE_TIME
if (loopcount >= 0 && ++loopcount > 100)
if (dmsg->loopcount >= 0 && ++(dmsg->loopcount) > 100)
{
loopcount = 0;
if (get_time() >= startime + LONGTIME)
dmsg->loopcount = 0;
if (get_time() >= dmsg->startime + LONGTIME)
{
longloopmessage();
loopcount = -1;
dmsg->message();
dmsg->loopcount = -1;
}
}
#else
if (loopcount >= 0 && ++loopcount > LONGLOOP)
if (dmsg->loopcount >= 0 && ++(dmsg->loopcount) > LONGLOOP)
{
longloopmessage();
loopcount = -1;
dmsg->message();
dmsg->loopcount = -1;
}
#endif
}
@ -240,15 +252,15 @@ static void longish(void)
* Turn off line numbers because the user has interrupted
* a lengthy line number calculation.
*/
static void abort_long(void)
static void abort_delayed_msg(struct delayed_msg *dmsg)
{
if (loopcount >= 0)
if (dmsg->loopcount >= 0)
return;
if (linenums == OPT_ONPLUS)
/*
* We were displaying line numbers, so need to repaint.
*/
screen_trashed = 1;
screen_trashed();
linenums = 0;
error("Line numbers turned off", NULL_PARG);
}
@ -262,6 +274,7 @@ public LINENUM find_linenum(POSITION pos)
struct linenum_info *p;
LINENUM linenum;
POSITION cpos;
struct delayed_msg dmsg;
if (!linenums)
/*
@ -299,10 +312,7 @@ public LINENUM find_linenum(POSITION pos)
* The decision is based on which way involves
* traversing fewer bytes in the file.
*/
#if HAVE_TIME
startime = get_time();
#endif
loopcount = 0;
start_delayed_msg(&dmsg, longloopmessage);
if (p == &anchor || pos - p->prev->pos < p->pos - pos)
{
/*
@ -316,14 +326,14 @@ public LINENUM find_linenum(POSITION pos)
/*
* Allow a signal to abort this loop.
*/
cpos = forw_raw_line(cpos, (char **)NULL, (int *)NULL);
cpos = forw_raw_line(cpos, NULL, NULL);
if (ABORT_SIGS()) {
abort_long();
abort_delayed_msg(&dmsg);
return (0);
}
if (cpos == NULL_POSITION)
return (0);
longish();
delayed_msg(&dmsg);
}
/*
* We might as well cache it.
@ -347,21 +357,20 @@ public LINENUM find_linenum(POSITION pos)
/*
* Allow a signal to abort this loop.
*/
cpos = back_raw_line(cpos, (char **)NULL, (int *)NULL);
cpos = back_raw_line(cpos, NULL, NULL);
if (ABORT_SIGS()) {
abort_long();
abort_delayed_msg(&dmsg);
return (0);
}
if (cpos == NULL_POSITION)
return (0);
longish();
delayed_msg(&dmsg);
}
/*
* We might as well cache it.
*/
add_lnum(linenum, cpos);
}
loopcount = 0;
return (linenum);
}
@ -403,7 +412,7 @@ public POSITION find_pos(LINENUM linenum)
/*
* Allow a signal to abort this loop.
*/
cpos = forw_raw_line(cpos, (char **)NULL, (int *)NULL);
cpos = forw_raw_line(cpos, NULL, NULL);
if (ABORT_SIGS())
return (NULL_POSITION);
if (cpos == NULL_POSITION)
@ -421,7 +430,7 @@ public POSITION find_pos(LINENUM linenum)
/*
* Allow a signal to abort this loop.
*/
cpos = back_raw_line(cpos, (char **)NULL, (int *)NULL);
cpos = back_raw_line(cpos, NULL, NULL);
if (ABORT_SIGS())
return (NULL_POSITION);
if (cpos == NULL_POSITION)
@ -458,6 +467,11 @@ public LINENUM currline(int where)
return (linenum);
}
static void detlenmessage(void)
{
ierror("Determining length of file", NULL_PARG);
}
/*
* Scan entire file, counting line numbers.
*/
@ -465,23 +479,28 @@ public void scan_eof(void)
{
POSITION pos = ch_zero();
LINENUM linenum = 0;
struct delayed_msg dmsg;
if (ch_seek(0))
return;
ierror("Determining length of file", NULL_PARG);
/*
* scanning_eof prevents the "Waiting for data" message from
* overwriting "Determining length of file".
*/
start_delayed_msg(&dmsg, detlenmessage);
scanning_eof = TRUE;
while (pos != NULL_POSITION)
{
/* For efficiency, only add one every 256 line numbers. */
if ((linenum++ % 256) == 0)
add_lnum(linenum, pos);
pos = forw_raw_line(pos, (char **)NULL, (int *)NULL);
pos = forw_raw_line(pos, NULL, NULL);
if (ABORT_SIGS())
{
abort_delayed_msg(&dmsg);
break;
}
delayed_msg(&dmsg);
}
scanning_eof = FALSE;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -32,7 +32,6 @@
#endif
#endif
extern int screen_trashed;
extern IFILE curr_ifile;
@ -42,11 +41,11 @@ extern IFILE curr_ifile;
* Pass the specified command to a shell to be executed.
* Like plain "system()", but handles resetting terminal modes, etc.
*/
public void lsystem(char *cmd, char *donemsg)
public void lsystem(constant char *cmd, constant char *donemsg)
{
int inp;
#if HAVE_SHELL
char *shell;
constant char *shell;
char *p;
#endif
IFILE save_ifile;
@ -136,7 +135,7 @@ public void lsystem(char *cmd, char *donemsg)
char *esccmd = shell_quote(cmd);
if (esccmd != NULL)
{
int len = (int) (strlen(shell) + strlen(esccmd) + 5);
size_t len = strlen(shell) + strlen(esccmd) + 5;
p = (char *) ecalloc(len, sizeof(char));
SNPRINTF3(p, len, "%s %s %s", shell, shell_coption(), esccmd);
free(esccmd);
@ -193,7 +192,7 @@ public void lsystem(char *cmd, char *donemsg)
flush();
}
init();
screen_trashed = 1;
screen_trashed();
#if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C
/*
@ -248,7 +247,7 @@ public void lsystem(char *cmd, char *donemsg)
* If the mark is on the current screen, or if the mark is ".",
* the whole current screen is piped.
*/
public int pipe_mark(int c, char *cmd)
public int pipe_mark(char c, constant char *cmd)
{
POSITION mpos, tpos, bpos;
@ -279,7 +278,7 @@ public int pipe_mark(int c, char *cmd)
* Create a pipe to the given shell command.
* Feed it the file contents between the positions spos and epos.
*/
public int pipe_data(char *cmd, POSITION spos, POSITION epos)
public int pipe_data(constant char *cmd, POSITION spos, POSITION epos)
{
FILE *f;
int c;
@ -353,7 +352,7 @@ public int pipe_data(char *cmd, POSITION spos, POSITION epos)
init_signals(1);
raw_mode(1);
init();
screen_trashed = 1;
screen_trashed();
#if defined(SIGWINCH) || defined(SIGWIND)
/* {{ Probably don't need this here. }} */
winch(0);

View file

@ -1,6 +1,6 @@
/* $FreeBSD$ */
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -17,10 +17,19 @@
#if MSDOS_COMPILER==WIN32C
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#if defined(MINGW) || defined(_MSC_VER)
#include <locale.h>
#include <shellapi.h>
#endif
public unsigned less_acp = CP_ACP;
#endif
#include "option.h"
public char * every_first_cmd = NULL;
public int new_file;
public lbool new_file;
public int is_tty;
public IFILE curr_ifile = NULL_IFILE;
public IFILE old_ifile = NULL_IFILE;
@ -28,20 +37,21 @@ public struct scrpos initial_scrpos;
public POSITION start_attnpos = NULL_POSITION;
public POSITION end_attnpos = NULL_POSITION;
public int wscroll;
public char * progname;
public constant char *progname;
public int quitting;
public int secure;
public int dohelp;
public char * init_header = NULL;
static int secure_allow_features;
#if LOGFILE
public int logfile = -1;
public int force_logfile = FALSE;
public lbool force_logfile = FALSE;
public char * namelogfile = NULL;
#endif
#if EDITOR
public char * editor;
public char * editproto;
public constant char * editor;
public constant char * editproto;
#endif
#if TAGS
@ -51,14 +61,13 @@ extern int jump_sline;
#endif
#ifdef WIN32
static char consoleTitle[256];
static wchar_t consoleTitle[256];
#endif
public int one_screen;
extern int less_is_more;
extern int missing_cap;
extern int know_dumb;
extern int pr_type;
extern int quit_if_one_screen;
extern int no_init;
extern int errmsgs;
@ -66,13 +75,178 @@ extern int redraw_on_quit;
extern int term_init_done;
extern int first_time;
#if MSDOS_COMPILER==WIN32C && (defined(MINGW) || defined(_MSC_VER))
/* malloc'ed 0-terminated utf8 of 0-terminated wide ws, or null on errors */
static char *utf8_from_wide(constant wchar_t *ws)
{
char *u8 = NULL;
int n = WideCharToMultiByte(CP_UTF8, 0, ws, -1, NULL, 0, NULL, NULL);
if (n > 0)
{
u8 = ecalloc(n, sizeof(char));
WideCharToMultiByte(CP_UTF8, 0, ws, -1, u8, n, NULL, NULL);
}
return u8;
}
/*
* similar to using UTF8 manifest to make the ANSI APIs UTF8, but dynamically
* with setlocale. unlike the manifest, argv and environ are already ACP, so
* make them UTF8. Additionally, this affects only the libc/crt API, and so
* e.g. fopen filename becomes UTF-8, but CreateFileA filename remains CP_ACP.
* CP_ACP remains the original codepage - use the dynamic less_acp instead.
* effective on win 10 1803 or later when compiled with ucrt, else no-op.
*/
static void try_utf8_locale(int *pargc, constant char ***pargv)
{
char *locale_orig = strdup(setlocale(LC_ALL, NULL));
wchar_t **wargv = NULL, *wenv, *wp;
constant char **u8argv;
char *u8e;
int i, n;
if (!setlocale(LC_ALL, ".UTF8"))
goto cleanup; /* not win10 1803+ or not ucrt */
/*
* wargv is before glob expansion. some ucrt builds may expand globs
* before main is entered, so n may be smaller than the original argc.
* that's ok, because later code at main expands globs anyway.
*/
wargv = CommandLineToArgvW(GetCommandLineW(), &n);
if (!wargv)
goto bad_args;
u8argv = (constant char **) ecalloc(n + 1, sizeof(char *));
for (i = 0; i < n; ++i)
{
if (!(u8argv[i] = utf8_from_wide(wargv[i])))
goto bad_args;
}
u8argv[n] = 0;
less_acp = CP_UTF8;
*pargc = n;
*pargv = u8argv; /* leaked on exit */
/* convert wide env to utf8 where we can, but don't abort on errors */
if ((wenv = GetEnvironmentStringsW()))
{
for (wp = wenv; *wp; wp += wcslen(wp) + 1)
{
if ((u8e = utf8_from_wide(wp)))
_putenv(u8e);
free(u8e); /* windows putenv makes a copy */
}
FreeEnvironmentStringsW(wenv);
}
goto cleanup;
bad_args:
error("WARNING: cannot use unicode arguments", NULL_PARG);
setlocale(LC_ALL, locale_orig);
cleanup:
free(locale_orig);
LocalFree(wargv);
}
#endif
static int security_feature_error(constant char *type, size_t len, constant char *name)
{
PARG parg;
size_t msglen = len + strlen(type) + 64;
char *msg = ecalloc(msglen, sizeof(char));
SNPRINTF3(msg, msglen, "LESSSECURE_ALLOW: %s feature name \"%.*s\"", type, (int) len, name);
parg.p_string = msg;
error("%s", &parg);
free(msg);
return 0;
}
/*
* Return the SF_xxx value of a secure feature given the name of the feature.
*/
static int security_feature(constant char *name, size_t len)
{
struct secure_feature { constant char *name; int sf_value; };
static struct secure_feature features[] = {
{ "edit", SF_EDIT },
{ "examine", SF_EXAMINE },
{ "glob", SF_GLOB },
{ "history", SF_HISTORY },
{ "lesskey", SF_LESSKEY },
{ "lessopen", SF_LESSOPEN },
{ "logfile", SF_LOGFILE },
{ "osc8", SF_OSC8_OPEN },
{ "pipe", SF_PIPE },
{ "shell", SF_SHELL },
{ "stop", SF_STOP },
{ "tags", SF_TAGS },
};
int i;
int match = -1;
for (i = 0; i < countof(features); i++)
{
if (strncmp(features[i].name, name, len) == 0)
{
if (match >= 0) /* name is ambiguous */
return security_feature_error("ambiguous", len, name);
match = i;
}
}
if (match < 0)
return security_feature_error("invalid", len, name);
return features[match].sf_value;
}
/*
* Set the secure_allow_features bitmask, which controls
* whether certain secure features are allowed.
*/
static void init_secure(void)
{
#if SECURE
secure_allow_features = 0;
#else
constant char *str = lgetenv("LESSSECURE");
if (isnullenv(str))
secure_allow_features = ~0; /* allow everything */
else
secure_allow_features = 0; /* allow nothing */
str = lgetenv("LESSSECURE_ALLOW");
if (!isnullenv(str))
{
for (;;)
{
constant char *estr;
while (*str == ' ' || *str == ',') ++str; /* skip leading spaces/commas */
if (*str == '\0') break;
estr = strchr(str, ',');
if (estr == NULL) estr = str + strlen(str);
while (estr > str && estr[-1] == ' ') --estr; /* trim trailing spaces */
secure_allow_features |= security_feature(str, ptr_diff(estr, str));
str = estr;
}
}
#endif
}
/*
* Entry point.
*/
int main(int argc, char *argv[])
int main(int argc, constant char *argv[])
{
IFILE ifile;
char *s;
constant char *s;
#if MSDOS_COMPILER==WIN32C && (defined(MINGW) || defined(_MSC_VER))
if (GetACP() != CP_UTF8) /* not using a UTF-8 manifest */
try_utf8_locale(&argc, &argv);
#endif
#ifdef __EMX__
_response(&argc, &argv);
@ -81,15 +255,7 @@ int main(int argc, char *argv[])
progname = *argv++;
argc--;
#if SECURE
secure = 1;
#else
secure = 0;
s = lgetenv("LESSSECURE");
if (!isnullenv(s))
secure = 1;
#endif
init_secure();
#ifdef WIN32
if (getenv("HOME") == NULL)
@ -110,7 +276,8 @@ int main(int argc, char *argv[])
putenv(env);
}
}
GetConsoleTitle(consoleTitle, sizeof(consoleTitle)/sizeof(char));
/* on failure, consoleTitle is already a valid empty string */
GetConsoleTitleW(consoleTitle, countof(consoleTitle));
#endif /* WIN32 */
/*
@ -121,7 +288,6 @@ int main(int argc, char *argv[])
init_mark();
init_cmds();
init_poll();
get_term();
init_charset();
init_line();
init_cmdhist();
@ -132,15 +298,14 @@ int main(int argc, char *argv[])
* If the name of the executable program is "more",
* act like LESS_IS_MORE is set.
*/
s = last_component(progname);
if (strcmp(s, "more") == 0)
if (strcmp(last_component(progname), "more") == 0) {
less_is_more = 1;
scan_option("-fG");
}
init_prompt();
if (less_is_more)
scan_option("-fG");
init_unsupport();
s = lgetenv(less_is_more ? "MORE" : "LESS");
if (s != NULL)
scan_option(s);
@ -170,6 +335,7 @@ int main(int argc, char *argv[])
if (less_is_more)
no_init = TRUE;
get_term();
expand_cmd_tables();
#if EDITOR
@ -202,7 +368,7 @@ int main(int argc, char *argv[])
* Expand the pattern and iterate over the expanded list.
*/
struct textlist tlist;
char *filename;
constant char *filename;
char *gfilename;
char *qfilename;
@ -294,6 +460,12 @@ int main(int argc, char *argv[])
one_screen = get_one_screen();
}
}
if (init_header != NULL)
{
opt_header(TOGGLE, init_header);
free(init_header);
init_header = NULL;
}
if (errmsgs > 0)
{
@ -318,13 +490,17 @@ int main(int argc, char *argv[])
* Copy a string to a "safe" place
* (that is, to a buffer allocated by calloc).
*/
public char * saven(constant char *s, size_t n)
{
char *p = (char *) ecalloc(n+1, sizeof(char));
strncpy(p, s, n);
p[n] = '\0';
return (p);
}
public char * save(constant char *s)
{
char *p;
p = (char *) ecalloc(strlen(s)+1, sizeof(char));
strcpy(p, s);
return (p);
return saven(s, strlen(s));
}
public void out_of_memory(void)
@ -337,7 +513,7 @@ public void out_of_memory(void)
* Allocate memory.
* Like calloc(), but never returns an error (NULL).
*/
public void * ecalloc(int count, unsigned int size)
public void * ecalloc(size_t count, size_t size)
{
void * p;
@ -357,16 +533,24 @@ public char * skipsp(char *s)
return (s);
}
/* {{ There must be a better way. }} */
public constant char * skipspc(constant char *s)
{
while (*s == ' ' || *s == '\t')
s++;
return (s);
}
/*
* See how many characters of two strings are identical.
* If uppercase is true, the first string must begin with an uppercase
* character; the remainder of the first string may be either case.
*/
public int sprefix(char *ps, char *s, int uppercase)
public size_t sprefix(constant char *ps, constant char *s, int uppercase)
{
int c;
int sc;
int len = 0;
char c;
char sc;
size_t len = 0;
for ( ; *s != '\0'; s++, ps++)
{
@ -374,7 +558,7 @@ public int sprefix(char *ps, char *s, int uppercase)
if (uppercase)
{
if (len == 0 && ASCII_IS_LOWER(c))
return (-1);
return (0);
if (ASCII_IS_UPPER(c))
c = ASCII_TO_LOWER(c);
}
@ -433,8 +617,16 @@ public void quit(int status)
close(2);
#endif
#ifdef WIN32
SetConsoleTitle(consoleTitle);
SetConsoleTitleW(consoleTitle);
#endif
close_getchr();
exit(status);
}
/*
* Are all the features in the features mask allowed by security?
*/
public int secure_allow(int features)
{
return ((secure_allow_features & features) == features);
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -73,7 +73,7 @@ public void init_mark(void)
switch (i) {
case MOUSEMARK: letter = '#'; break;
case LASTMARK: letter = '\''; break;
default: letter = (i < 26) ? 'a'+i : 'A'+i-26; break;
default: letter = (char) ((i < 26) ? 'a'+i : 'A'+i-26); break;
}
marks[i].m_letter = letter;
cmark(&marks[i], NULL_IFILE, NULL_POSITION, -1);
@ -104,7 +104,7 @@ static void mark_get_ifile(struct mark *m)
/*
* Return the user mark struct identified by a character.
*/
static struct mark * getumark(LWCHAR c)
static struct mark * getumark(char c)
{
PARG parg;
if (c >= 'a' && c <= 'z')
@ -125,7 +125,7 @@ static struct mark * getumark(LWCHAR c)
* 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(LWCHAR c)
static struct mark * getmark(char c)
{
struct mark *m;
static struct mark sm;
@ -185,7 +185,7 @@ static struct mark * getmark(LWCHAR c)
/*
* Is a mark letter invalid?
*/
public int badmark(LWCHAR c)
public int badmark(char c)
{
return (getmark(c) == NULL);
}
@ -193,7 +193,7 @@ public int badmark(LWCHAR c)
/*
* Set a user-defined mark.
*/
public void setmark(LWCHAR c, int where)
public void setmark(char c, int where)
{
struct mark *m;
struct scrpos scrpos;
@ -214,7 +214,7 @@ public void setmark(LWCHAR c, int where)
/*
* Clear a user-defined mark.
*/
public void clrmark(LWCHAR c)
public void clrmark(char c)
{
struct mark *m;
@ -249,7 +249,7 @@ public void lastmark(void)
/*
* Go to a mark.
*/
public void gomark(LWCHAR c)
public void gomark(char c)
{
struct mark *m;
struct scrpos scrpos;
@ -290,7 +290,7 @@ public void gomark(LWCHAR c)
* is associated with, but this doesn't matter much,
* because it's always the first non-blank line on the screen.
*/
public POSITION markpos(LWCHAR c)
public POSITION markpos(char c)
{
struct mark *m;
@ -311,15 +311,15 @@ public POSITION markpos(LWCHAR c)
*/
public char posmark(POSITION pos)
{
int i;
unsigned char 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);
if (i < 26) return (char) ('a' + i);
if (i < 26*2) return (char) ('A' + (i - 26));
return '#';
}
}
@ -345,7 +345,7 @@ public void unmark(IFILE ifile)
public void mark_check_ifile(IFILE ifile)
{
int i;
char *filename = get_real_filename(ifile);
constant char *filename = get_real_filename(ifile);
for (i = 0; i < NMARKS; i++)
{
@ -366,7 +366,7 @@ public void mark_check_ifile(IFILE ifile)
/*
* Save marks to history file.
*/
public void save_marks(FILE *fout, char *hdr)
public void save_marks(FILE *fout, constant char *hdr)
{
int i;
@ -376,7 +376,7 @@ public void save_marks(FILE *fout, char *hdr)
fprintf(fout, "%s\n", hdr);
for (i = 0; i < NMARKS; i++)
{
char *filename;
constant 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)
@ -394,7 +394,7 @@ public void save_marks(FILE *fout, char *hdr)
/*
* Restore one mark from the history file.
*/
public void restore_mark(char *line)
public void restore_mark(constant char *line)
{
struct mark *m;
int ln;
@ -408,7 +408,7 @@ public void restore_mark(char *line)
if (m == NULL)
return;
skip_whitespace;
ln = lstrtoi(line, &line, 10);
ln = lstrtoic(line, &line, 10);
if (ln < 0)
return;
if (ln < 1)
@ -416,7 +416,7 @@ public void restore_mark(char *line)
if (ln > sc_height)
ln = sc_height;
skip_whitespace;
pos = lstrtopos(line, &line, 10);
pos = lstrtoposc(line, &line, 10);
if (pos < 0)
return;
skip_whitespace;

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -26,17 +26,15 @@
#include "less.h"
#include "option.h"
#include "position.h"
extern int nbufs;
extern int bufspace;
extern int pr_type;
extern int plusoption;
extern lbool plusoption;
extern int swindow;
extern int sc_width;
extern int sc_height;
extern int secure;
extern int dohelp;
extern int is_tty;
extern char openquote;
extern char closequote;
extern char *prproto[];
@ -50,7 +48,9 @@ extern int jump_sline;
extern long jump_sline_fraction;
extern int shift_count;
extern long shift_count_fraction;
extern char rscroll_char;
extern int match_shift;
extern long match_shift_fraction;
extern LWCHAR rscroll_char;
extern int rscroll_attr;
extern int mousecap;
extern int wheel_lines;
@ -67,9 +67,13 @@ extern int tabstops[];
extern int ntabstops;
extern int tabdefault;
extern char intr_char;
extern int nosearch_header_lines;
extern int nosearch_header_cols;
extern POSITION header_start_pos;
extern char *init_header;
#if LOGFILE
extern char *namelogfile;
extern int force_logfile;
extern lbool force_logfile;
extern int logfile;
#endif
#if TAGS
@ -78,19 +82,23 @@ extern char *tags;
extern char ztags[];
#endif
#if LESSTEST
extern char *ttyin_name;
extern constant char *ttyin_name;
extern int is_tty;
#endif /*LESSTEST*/
#if MSDOS_COMPILER
extern int nm_fg_color, nm_bg_color;
extern int bo_fg_color, bo_bg_color;
extern int ul_fg_color, ul_bg_color;
extern int so_fg_color, so_bg_color;
extern int bl_fg_color, bl_bg_color;
extern int nm_fg_color, nm_bg_color, nm_attr;
extern int bo_fg_color, bo_bg_color, bo_attr;
extern int ul_fg_color, ul_bg_color, ul_attr;
extern int so_fg_color, so_bg_color, so_attr;
extern int bl_fg_color, bl_bg_color, bl_attr;
extern int sgr_mode;
#if MSDOS_COMPILER==WIN32C
#ifndef COMMON_LVB_UNDERSCORE
#define COMMON_LVB_UNDERSCORE 0x8000
#endif
#ifndef COMMON_LVB_REVERSE_VIDEO
#define COMMON_LVB_REVERSE_VIDEO 0x4000
#endif
#endif
#endif
@ -99,12 +107,12 @@ extern int sgr_mode;
/*
* Handler for -o option.
*/
public void opt_o(int type, char *s)
public void opt_o(int type, constant char *s)
{
PARG parg;
char *filename;
if (secure)
if (!secure_allow(SF_LOGFILE))
{
error("log file support is not available", NULL_PARG);
return;
@ -125,7 +133,7 @@ public void opt_o(int type, char *s)
error("Log file is already in use", NULL_PARG);
return;
}
s = skipsp(s);
s = skipspc(s);
if (namelogfile != NULL)
free(namelogfile);
filename = lglob(s);
@ -149,122 +157,109 @@ public void opt_o(int type, char *s)
/*
* Handler for -O option.
*/
public void opt__O(int type, char *s)
public void opt__O(int type, constant char *s)
{
force_logfile = TRUE;
opt_o(type, s);
}
#endif
static int toggle_fraction(int *num, long *frac, constant char *s, constant char *printopt, void (*calc)(void))
{
lbool err;
if (s == NULL)
{
(*calc)();
} else if (*s == '.')
{
long tfrac;
s++;
tfrac = getfraction(&s, printopt, &err);
if (err)
{
error("Invalid fraction", NULL_PARG);
return -1;
}
*frac = tfrac;
(*calc)();
} else
{
int tnum = getnumc(&s, printopt, &err);
if (err)
{
error("Invalid number", NULL_PARG);
return -1;
}
*frac = -1;
*num = tnum;
}
return 0;
}
static void query_fraction(int value, long fraction, constant char *int_msg, constant char *frac_msg)
{
PARG parg;
if (fraction < 0)
{
parg.p_int = value;
error(int_msg, &parg);
} else
{
char buf[INT_STRLEN_BOUND(long)+2];
size_t len;
SNPRINTF1(buf, sizeof(buf), ".%06ld", fraction);
len = strlen(buf);
while (len > 2 && buf[len-1] == '0')
len--;
buf[len] = '\0';
parg.p_string = buf;
error(frac_msg, &parg);
}
}
/*
* Handlers for -j option.
*/
public void opt_j(int type, char *s)
public void opt_j(int type, constant char *s)
{
PARG parg;
int len;
int err;
switch (type)
{
case INIT:
case TOGGLE:
if (*s == '.')
{
s++;
jump_sline_fraction = getfraction(&s, "j", &err);
if (err)
error("Invalid line fraction", NULL_PARG);
else
calc_jump_sline();
} else
{
int sline = getnum(&s, "j", &err);
if (err)
error("Invalid line number", NULL_PARG);
else
{
jump_sline = sline;
jump_sline_fraction = -1;
}
}
toggle_fraction(&jump_sline, &jump_sline_fraction,
s, "j", calc_jump_sline);
break;
case QUERY:
if (jump_sline_fraction < 0)
{
parg.p_int = jump_sline;
error("Position target at screen line %d", &parg);
} else
{
char buf[INT_STRLEN_BOUND(long)+2];
SNPRINTF1(buf, sizeof(buf), ".%06ld", jump_sline_fraction);
len = (int) strlen(buf);
while (len > 2 && buf[len-1] == '0')
len--;
buf[len] = '\0';
parg.p_string = buf;
error("Position target at screen position %s", &parg);
}
query_fraction(jump_sline, jump_sline_fraction,
"Position target at screen line %d", "Position target at screen position %s");
break;
}
}
public void calc_jump_sline(void)
{
if (jump_sline_fraction < 0)
return;
jump_sline = (int) muldiv(sc_height, jump_sline_fraction, NUM_FRAC_DENOM);
if (jump_sline_fraction >= 0)
jump_sline = (int) muldiv(sc_height, jump_sline_fraction, NUM_FRAC_DENOM);
if (jump_sline <= header_lines)
jump_sline = header_lines + 1;
}
/*
* Handlers for -# option.
*/
public void opt_shift(int type, char *s)
public void opt_shift(int type, constant char *s)
{
PARG parg;
int len;
int err;
switch (type)
{
case INIT:
case TOGGLE:
if (*s == '.')
{
s++;
shift_count_fraction = getfraction(&s, "#", &err);
if (err)
error("Invalid column fraction", NULL_PARG);
else
calc_shift_count();
} else
{
int hs = getnum(&s, "#", &err);
if (err)
error("Invalid column number", NULL_PARG);
else
{
shift_count = hs;
shift_count_fraction = -1;
}
}
toggle_fraction(&shift_count, &shift_count_fraction,
s, "#", calc_shift_count);
break;
case QUERY:
if (shift_count_fraction < 0)
{
parg.p_int = shift_count;
error("Horizontal shift %d columns", &parg);
} else
{
char buf[INT_STRLEN_BOUND(long)+2];
SNPRINTF1(buf, sizeof(buf), ".%06ld", shift_count_fraction);
len = (int) strlen(buf);
while (len > 2 && buf[len-1] == '0')
len--;
buf[len] = '\0';
parg.p_string = buf;
error("Horizontal shift %s of screen width", &parg);
}
query_fraction(shift_count, shift_count_fraction,
"Horizontal shift %d columns", "Horizontal shift %s of screen width");
break;
}
}
@ -277,7 +272,7 @@ public void calc_shift_count(void)
}
#if USERFILE
public void opt_k(int type, char *s)
public void opt_k(int type, constant char *s)
{
PARG parg;
@ -294,7 +289,7 @@ public void opt_k(int type, char *s)
}
#if HAVE_LESSKEYSRC
public void opt_ks(int type, char *s)
public void opt_ks(int type, constant char *s)
{
PARG parg;
@ -309,14 +304,41 @@ public void opt_ks(int type, char *s)
break;
}
}
public void opt_kc(int type, constant char *s)
{
switch (type)
{
case INIT:
if (lesskey_content(s, 0))
{
error("Error in lesskey content", NULL_PARG);
}
break;
}
}
#endif /* HAVE_LESSKEYSRC */
#endif /* USERFILE */
/*
* Handler for -S option.
*/
public void opt__S(int type, constant char *s)
{
switch (type)
{
case TOGGLE:
pos_rehead();
break;
}
}
#if TAGS
/*
* Handler for -t option.
*/
public void opt_t(int type, char *s)
public void opt_t(int type, constant char *s)
{
IFILE save_ifile;
POSITION pos;
@ -328,12 +350,12 @@ public void opt_t(int type, char *s)
/* Do the rest in main() */
break;
case TOGGLE:
if (secure)
if (!secure_allow(SF_TAGS))
{
error("tags support is not available", NULL_PARG);
break;
}
findtag(skipsp(s));
findtag(skipspc(s));
save_ifile = save_curr_ifile();
/*
* Try to open the file containing the tag
@ -354,7 +376,7 @@ public void opt_t(int type, char *s)
/*
* Handler for -T option.
*/
public void opt__T(int type, char *s)
public void opt__T(int type, constant char *s)
{
PARG parg;
char *filename;
@ -365,7 +387,7 @@ public void opt__T(int type, char *s)
tags = save(s);
break;
case TOGGLE:
s = skipsp(s);
s = skipspc(s);
if (tags != NULL && tags != ztags)
free(tags);
filename = lglob(s);
@ -383,7 +405,7 @@ public void opt__T(int type, char *s)
/*
* Handler for -p option.
*/
public void opt_p(int type, char *s)
public void opt_p(int type, constant char *s)
{
switch (type)
{
@ -407,7 +429,7 @@ public void opt_p(int type, char *s)
*/
ungetsc("/");
ungetsc(s);
ungetcc_back(CHAR_END_COMMAND);
ungetcc_end_command();
}
break;
}
@ -416,7 +438,7 @@ public void opt_p(int type, char *s)
/*
* Handler for -P option.
*/
public void opt__P(int type, char *s)
public void opt__P(int type, constant char *s)
{
char **proto;
PARG parg;
@ -452,7 +474,7 @@ public void opt__P(int type, char *s)
* Handler for the -b option.
*/
/*ARGSUSED*/
public void opt_b(int type, char *s)
public void opt_b(int type, constant char *s)
{
switch (type)
{
@ -461,7 +483,7 @@ public void opt_b(int type, char *s)
/*
* Set the new number of buffers.
*/
ch_setbufspace(bufspace);
ch_setbufspace((ssize_t) bufspace);
break;
case QUERY:
break;
@ -472,7 +494,7 @@ public void opt_b(int type, char *s)
* Handler for the -i option.
*/
/*ARGSUSED*/
public void opt_i(int type, char *s)
public void opt_i(int type, constant char *s)
{
switch (type)
{
@ -489,7 +511,7 @@ public void opt_i(int type, char *s)
* Handler for the -V option.
*/
/*ARGSUSED*/
public void opt__V(int type, char *s)
public void opt__V(int type, constant char *s)
{
switch (type)
{
@ -506,7 +528,7 @@ public void opt__V(int type, char *s)
putstr(" regular expressions)\n");
{
char constant *copyright =
"Copyright (C) 1984-2023 Mark Nudelman\n\n";
"Copyright (C) 1984-2024 Mark Nudelman\n\n";
putstr(copyright);
}
if (version[strlen(version)-1] == 'x')
@ -531,40 +553,26 @@ public void opt__V(int type, char *s)
/*
* Parse an MSDOS color descriptor.
*/
static void colordesc(char *s, int *fg_color, int *bg_color)
static void colordesc(constant char *s, int *fg_color, int *bg_color, int *dattr)
{
int fg, bg;
#if MSDOS_COMPILER==WIN32C
int ul = 0;
if (*s == 'u')
{
ul = COMMON_LVB_UNDERSCORE;
s++;
if (*s == '\0')
{
*fg_color = nm_fg_color | ul;
*bg_color = nm_bg_color;
return;
}
}
#endif
if (parse_color(s, &fg, &bg) == CT_NULL)
CHAR_ATTR attr;
if (parse_color(s, &fg, &bg, &attr) == CT_NULL)
{
PARG p;
p.p_string = s;
error("Invalid color string \"%s\"", &p);
} else
{
if (fg == CV_NOCHANGE)
fg = nm_fg_color;
if (bg == CV_NOCHANGE)
bg = nm_bg_color;
#if MSDOS_COMPILER==WIN32C
fg |= ul;
#endif
*fg_color = fg;
*bg_color = bg;
*dattr = 0;
#if MSDOS_COMPILER==WIN32C
if (attr & CATTR_UNDERLINE)
*dattr |= COMMON_LVB_UNDERSCORE;
if (attr & CATTR_STANDOUT)
*dattr |= COMMON_LVB_REVERSE_VIDEO;
#endif
}
}
#endif
@ -599,7 +607,7 @@ static int color_from_namechar(char namechar)
* Handler for the -D option.
*/
/*ARGSUSED*/
public void opt_D(int type, char *s)
public void opt_D(int type, constant char *s)
{
PARG p;
int attr;
@ -634,23 +642,24 @@ public void opt_D(int type, char *s)
switch (attr)
{
case AT_NORMAL:
colordesc(s, &nm_fg_color, &nm_bg_color);
colordesc(s, &nm_fg_color, &nm_bg_color, &nm_attr);
break;
case AT_BOLD:
colordesc(s, &bo_fg_color, &bo_bg_color);
colordesc(s, &bo_fg_color, &bo_bg_color, &bo_attr);
break;
case AT_UNDERLINE:
colordesc(s, &ul_fg_color, &ul_bg_color);
colordesc(s, &ul_fg_color, &ul_bg_color, &ul_attr);
break;
case AT_BLINK:
colordesc(s, &bl_fg_color, &bl_bg_color);
colordesc(s, &bl_fg_color, &bl_bg_color, &bl_attr);
break;
case AT_STANDOUT:
colordesc(s, &so_fg_color, &so_bg_color);
colordesc(s, &so_fg_color, &so_bg_color, &so_attr);
break;
}
if (type == TOGGLE)
{
init_win_colors();
at_enter(AT_STANDOUT);
at_exit();
}
@ -674,21 +683,21 @@ public void opt_D(int type, char *s)
/*
*/
public void set_tabs(char *s, int len)
public void set_tabs(constant char *s, size_t len)
{
int i;
char *es = s + len;
constant char *es = s + len;
/* Start at 1 because tabstops[0] is always zero. */
for (i = 1; i < TABSTOP_MAX; )
{
int n = 0;
int v = FALSE;
lbool v = FALSE;
while (s < es && *s == ' ')
s++;
for (; s < es && *s >= '0' && *s <= '9'; s++)
{
v |= ckd_mul(&n, n, 10);
v |= ckd_add(&n, n, *s - '0');
v = v || ckd_mul(&n, n, 10);
v = v || ckd_add(&n, n, *s - '0');
}
if (!v && n > tabstops[i-1])
tabstops[i++] = n;
@ -706,7 +715,7 @@ public void set_tabs(char *s, int len)
/*
* Handler for the -x option.
*/
public void opt_x(int type, char *s)
public void opt_x(int type, constant char *s)
{
char msg[60+((INT_STRLEN_BOUND(int)+1)*TABSTOP_MAX)];
int i;
@ -742,7 +751,7 @@ public void opt_x(int type, char *s)
/*
* Handler for the -" option.
*/
public void opt_quote(int type, char *s)
public void opt_quote(int type, constant char *s)
{
char buf[3];
PARG parg;
@ -781,7 +790,7 @@ public void opt_quote(int type, char *s)
* Handler for the --rscroll option.
*/
/*ARGSUSED*/
public void opt_rscroll(int type, char *s)
public void opt_rscroll(int type, constant char *s)
{
PARG p;
@ -789,7 +798,7 @@ public void opt_rscroll(int type, char *s)
{
case INIT:
case TOGGLE: {
char *fmt;
constant char *fmt;
int attr = AT_STANDOUT;
setfmt(s, &fmt, &attr, "*s>", FALSE);
if (strcmp(fmt, "-") == 0)
@ -797,12 +806,21 @@ public void opt_rscroll(int type, char *s)
rscroll_char = 0;
} else
{
rscroll_char = *fmt ? *fmt : '>';
rscroll_attr = attr|AT_COLOR_RSCROLL;
if (*fmt == '\0')
rscroll_char = '>';
else
{
LWCHAR ch = step_charc(&fmt, +1, fmt+strlen(fmt));
if (pwidth(ch, rscroll_attr, 0, 0) > 1)
error("cannot set rscroll to a wide character", NULL_PARG);
else
rscroll_char = ch;
}
}
break; }
case QUERY: {
p.p_string = rscroll_char ? prchar(rscroll_char) : "-";
p.p_string = rscroll_char ? prchar((LWCHAR) rscroll_char) : "-";
error("rscroll character is %s", &p);
break; }
}
@ -813,7 +831,7 @@ public void opt_rscroll(int type, char *s)
* If from the command line, exit immediately.
*/
/*ARGSUSED*/
public void opt_query(int type, char *s)
public void opt_query(int type, constant char *s)
{
switch (type)
{
@ -826,11 +844,35 @@ public void opt_query(int type, char *s)
}
}
/*ARGSUSED*/
public void opt_match_shift(int type, constant char *s)
{
switch (type)
{
case INIT:
case TOGGLE:
toggle_fraction(&match_shift, &match_shift_fraction,
s, "--match-shift", calc_match_shift);
break;
case QUERY:
query_fraction(match_shift, match_shift_fraction,
"Search match shift is %d", "Search match shift is %s of screen width");
break;
}
}
public void calc_match_shift(void)
{
if (match_shift_fraction < 0)
return;
match_shift = (int) muldiv(sc_width, match_shift_fraction, NUM_FRAC_DENOM);
}
/*
* Handler for the --mouse option.
*/
/*ARGSUSED*/
public void opt_mousecap(int type, char *s)
public void opt_mousecap(int type, constant char *s)
{
switch (type)
{
@ -850,7 +892,7 @@ public void opt_mousecap(int type, char *s)
* Handler for the --wheel-lines option.
*/
/*ARGSUSED*/
public void opt_wheel_lines(int type, char *s)
public void opt_wheel_lines(int type, constant char *s)
{
switch (type)
{
@ -868,7 +910,7 @@ public void opt_wheel_lines(int type, char *s)
* Handler for the --line-number-width option.
*/
/*ARGSUSED*/
public void opt_linenum_width(int type, char *s)
public void opt_linenum_width(int type, constant char *s)
{
PARG parg;
@ -892,7 +934,7 @@ public void opt_linenum_width(int type, char *s)
* Handler for the --status-column-width option.
*/
/*ARGSUSED*/
public void opt_status_col_width(int type, char *s)
public void opt_status_col_width(int type, constant char *s)
{
PARG parg;
@ -916,7 +958,7 @@ public void opt_status_col_width(int type, char *s)
* Handler for the --file-size option.
*/
/*ARGSUSED*/
public void opt_filesize(int type, char *s)
public void opt_filesize(int type, constant char *s)
{
switch (type)
{
@ -934,7 +976,7 @@ public void opt_filesize(int type, char *s)
* Handler for the --intr option.
*/
/*ARGSUSED*/
public void opt_intr(int type, char *s)
public void opt_intr(int type, constant char *s)
{
PARG p;
@ -947,56 +989,104 @@ public void opt_intr(int type, char *s)
intr_char = CONTROL(s[1]);
break;
case QUERY: {
p.p_string = prchar(intr_char);
p.p_string = prchar((LWCHAR) intr_char);
error("interrupt character is %s", &p);
break; }
}
}
/*
* Return the next number from a comma-separated list.
* Return -1 if the list entry is missing or empty.
* Updates *sp to point to the first char of the next number in the list.
*/
public int next_cnum(constant char **sp, constant char *printopt, constant char *errmsg, lbool *errp)
{
int n;
*errp = FALSE;
if (**sp == '\0') /* at end of line */
return -1;
if (**sp == ',') /* that's the next comma; we have an empty string */
{
++(*sp);
return -1;
}
n = getnumc(sp, printopt, errp);
if (*errp)
{
PARG parg;
parg.p_string = errmsg;
error("invalid %s", &parg);
return -1;
}
if (**sp == ',')
++(*sp);
return n;
}
/*
* Parse a parameter to the --header option.
* Value is "L,C,N", where each field is a decimal number or empty.
*/
static lbool parse_header(constant char *s, int *lines, int *cols, POSITION *start_pos)
{
int n;
lbool err;
if (*s == '-')
s = "0,0";
n = next_cnum(&s, "header", "number of lines", &err);
if (err) return FALSE;
if (n >= 0) *lines = n;
n = next_cnum(&s, "header", "number of columns", &err);
if (err) return FALSE;
if (n >= 0) *cols = n;
n = next_cnum(&s, "header", "line number", &err);
if (err) return FALSE;
if (n > 0)
{
LINENUM lnum = (LINENUM) n;
if (lnum < 1) lnum = 1;
*start_pos = find_pos(lnum);
}
return TRUE;
}
/*
* Handler for the --header option.
*/
/*ARGSUSED*/
public void opt_header(int type, char *s)
public void opt_header(int type, constant char *s)
{
int err;
int n;
switch (type)
{
case INIT:
case TOGGLE:
header_lines = 0;
header_cols = 0;
if (*s != ',')
{
n = getnum(&s, "header", &err);
if (err)
{
error("invalid number of lines", NULL_PARG);
return;
}
header_lines = n;
}
if (*s == ',')
{
++s;
n = getnum(&s, "header", &err);
if (err)
error("invalid number of columns", NULL_PARG);
else
header_cols = n;
}
break;
case QUERY:
{
char buf[2*INT_STRLEN_BOUND(int)+2];
PARG parg;
SNPRINTF2(buf, sizeof(buf), "%d,%d", header_lines, header_cols);
parg.p_string = buf;
error("header (lines,columns) is %s", &parg);
}
/* Can't call parse_header now because input file is not yet opened,
* so find_pos won't work. */
init_header = save(s);
break;
case TOGGLE: {
int lines = header_lines;
int cols = header_cols;
POSITION start_pos = (type == INIT) ? ch_zero() : position(TOP);
if (start_pos == NULL_POSITION) start_pos = ch_zero();
if (!parse_header(s, &lines, &cols, &start_pos))
break;
header_lines = lines;
header_cols = cols;
set_header(start_pos);
calc_jump_sline();
break; }
case QUERY: {
char buf[3*INT_STRLEN_BOUND(long)+3];
PARG parg;
SNPRINTF3(buf, sizeof(buf), "%ld,%ld,%ld", (long) header_lines, (long) header_cols, (long) find_linenum(header_start_pos));
parg.p_string = buf;
error("Header (lines,columns,line-number) is %s", &parg);
break; }
}
}
@ -1004,7 +1094,7 @@ public void opt_header(int type, char *s)
* Handler for the --search-options option.
*/
/*ARGSUSED*/
public void opt_search_type(int type, char *s)
public void opt_search_type(int type, constant char *s)
{
int st;
PARG parg;
@ -1052,7 +1142,7 @@ public void opt_search_type(int type, char *s)
if (def_search_type & SRCH_WRAP) *bp++ = 'W';
for (i = 1; i <= NUM_SEARCH_COLORS; i++)
if (def_search_type & SRCH_SUBSEARCH(i))
*bp++ = '0'+i;
*bp++ = (char) ('0'+i);
if (bp == buf)
*bp++ = '-';
*bp = '\0';
@ -1062,12 +1152,55 @@ public void opt_search_type(int type, char *s)
}
}
/*
* Handler for the --no-search-headers, --no-search-header-lines
* and --no-search-header-cols options.
*/
static void do_nosearch_headers(int type, int no_header_lines, int no_header_cols)
{
switch (type)
{
case INIT:
case TOGGLE:
nosearch_header_lines = no_header_lines;
nosearch_header_cols = no_header_cols;
break;
case QUERY:
if (nosearch_header_lines && nosearch_header_cols)
error("Search does not include header lines or columns", NULL_PARG);
else if (nosearch_header_lines)
error("Search includes header columns but not header lines", NULL_PARG);
else if (nosearch_header_cols)
error("Search includes header lines but not header columns", NULL_PARG);
else
error("Search includes header lines and columns", NULL_PARG);
}
}
/*ARGSUSED*/
public void opt_nosearch_headers(int type, constant char *s)
{
do_nosearch_headers(type, 1, 1);
}
/*ARGSUSED*/
public void opt_nosearch_header_lines(int type, constant char *s)
{
do_nosearch_headers(type, 1, 0);
}
/*ARGSUSED*/
public void opt_nosearch_header_cols(int type, constant char *s)
{
do_nosearch_headers(type, 0, 1);
}
#if LESSTEST
/*
* Handler for the --tty option.
*/
/*ARGSUSED*/
public void opt_ttyin_name(int type, char *s)
public void opt_ttyin_name(int type, constant char *s)
{
switch (type)
{

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -21,12 +21,11 @@
#include "option.h"
static struct loption *pendopt;
public int plusoption = FALSE;
public lbool plusoption = FALSE;
static char *optstring(char *s, char **p_str, char *printopt, char *validchars);
static constant char *optstring(constant char *s, char **p_str, constant char *printopt, constant char *validchars);
static int flip_triple(int val, int lc);
extern int screen_trashed;
extern int less_is_more;
extern int quit_at_eof;
extern char *every_first_cmd;
@ -35,7 +34,7 @@ extern int opt_use_backslash;
/*
* Return a printable description of an option.
*/
static char * opt_desc(struct loption *o)
static constant char * opt_desc(struct loption *o)
{
static char buf[OPTNAME_MAX + 10];
if (o->oletter == OLETTER_NONE)
@ -49,11 +48,11 @@ static char * opt_desc(struct loption *o)
* Return a string suitable for printing as the "name" of an option.
* For example, if the option letter is 'x', just return "-x".
*/
public char * propt(int c)
public constant char * propt(char c)
{
static char buf[MAX_PRCHAR_LEN+2];
sprintf(buf, "-%s", prchar(c));
sprintf(buf, "-%s", prchar((LWCHAR) c));
return (buf);
}
@ -61,16 +60,16 @@ public char * propt(int c)
* Scan an argument (either from the command line or from the
* LESS environment variable) and process it.
*/
public void scan_option(char *s)
public void scan_option(constant char *s)
{
struct loption *o;
int optc;
char *optname;
char *printopt;
char optc;
constant char *optname;
constant char *printopt;
char *str;
int set_default;
lbool set_default;
int lc;
int err;
lbool ambig;
PARG parg;
if (s == NULL)
@ -85,15 +84,18 @@ public void scan_option(char *s)
*/
if (pendopt != NULL)
{
switch (pendopt->otype & OTYPE)
if (!(pendopt->otype & UNSUPPORTED))
{
case STRING:
(*pendopt->ofunc)(INIT, s);
break;
case NUMBER:
printopt = opt_desc(pendopt);
*(pendopt->ovar) = getnum(&s, printopt, (int*)NULL);
break;
switch (pendopt->otype & OTYPE)
{
case STRING:
(*pendopt->ofunc)(INIT, s);
break;
case NUMBER:
printopt = opt_desc(pendopt);
*(pendopt->ovar) = getnumc(&s, printopt, NULL);
break;
}
}
pendopt = NULL;
return;
@ -118,18 +120,19 @@ public void scan_option(char *s)
* "--" indicates an option name instead of a letter.
*/
if (*s == '-')
{
optname = ++s;
break;
}
/*
* "-+" means set these options back to their defaults.
* (They may have been set otherwise by previous
* options.)
* "-+" or "--+" means set these options back to their defaults.
* (They may have been set otherwise by previous options.)
*/
set_default = (*s == '+');
if (set_default)
s++;
if (optname != NULL)
{
optname = s;
break;
}
continue;
case '+':
/*
@ -151,7 +154,7 @@ public void scan_option(char *s)
} else
{
ungetsc(str);
ungetcc_back(CHAR_END_COMMAND);
ungetcc_end_command();
}
free(str);
continue;
@ -175,7 +178,7 @@ public void scan_option(char *s)
* Not a special case.
* Look up the option letter in the option table.
*/
err = 0;
ambig = FALSE;
if (optname == NULL)
{
printopt = propt(optc);
@ -185,7 +188,7 @@ public void scan_option(char *s)
{
printopt = optname;
lc = ASCII_IS_LOWER(optname[0]);
o = findopt_name(&optname, NULL, &err);
o = findopt_name(&optname, NULL, &ambig);
s = optname;
optname = NULL;
if (*s == '\0' || *s == ' ')
@ -221,7 +224,7 @@ public void scan_option(char *s)
if (o == NULL)
{
parg.p_string = printopt;
if (err == OPT_AMBIG)
if (ambig)
error("%s is an ambiguous abbreviation (\"less --help\" for help)",
&parg);
else
@ -234,16 +237,26 @@ public void scan_option(char *s)
switch (o->otype & OTYPE)
{
case BOOL:
if (set_default)
*(o->ovar) = o->odefault;
else
*(o->ovar) = ! o->odefault;
if (o->otype & UNSUPPORTED)
break;
if (o->ovar != NULL)
{
if (set_default)
*(o->ovar) = o->odefault;
else
*(o->ovar) = ! o->odefault;
}
break;
case TRIPLE:
if (set_default)
*(o->ovar) = o->odefault;
else
*(o->ovar) = flip_triple(o->odefault, lc);
if (o->otype & UNSUPPORTED)
break;
if (o->ovar != NULL)
{
if (set_default)
*(o->ovar) = o->odefault;
else
*(o->ovar) = flip_triple(o->odefault, lc);
}
break;
case STRING:
if (*s == '\0')
@ -273,13 +286,15 @@ public void scan_option(char *s)
pendopt = o;
return;
}
*(o->ovar) = getnum(&s, printopt, (int*)NULL);
if (o->otype & UNSUPPORTED)
break;
*(o->ovar) = getnumc(&s, printopt, NULL);
break;
}
/*
* If the option has a handling function, call it.
*/
if (o->ofunc != NULL)
if (o->ofunc != NULL && !(o->otype & UNSUPPORTED))
(*o->ofunc)(INIT, str);
if (str != NULL)
free(str);
@ -295,11 +310,11 @@ public void scan_option(char *s)
* OPT_UNSET set to the default value
* OPT_SET set to the inverse of the default value
*/
public void toggle_option(struct loption *o, int lower, char *s, int how_toggle)
public void toggle_option(struct loption *o, int lower, constant char *s, int how_toggle)
{
int num;
int no_prompt;
int err;
lbool err;
PARG parg;
no_prompt = (how_toggle & OPT_NO_PROMPT);
@ -342,7 +357,7 @@ public void toggle_option(struct loption *o, int lower, char *s, int how_toggle)
#if HILITE_SEARCH
if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
repaint_hilite(0);
repaint_hilite(FALSE);
#endif
/*
@ -356,17 +371,20 @@ public void toggle_option(struct loption *o, int lower, char *s, int how_toggle)
/*
* Boolean.
*/
switch (how_toggle)
if (o->ovar != NULL)
{
case OPT_TOGGLE:
*(o->ovar) = ! *(o->ovar);
break;
case OPT_UNSET:
*(o->ovar) = o->odefault;
break;
case OPT_SET:
*(o->ovar) = ! o->odefault;
break;
switch (how_toggle)
{
case OPT_TOGGLE:
*(o->ovar) = ! *(o->ovar);
break;
case OPT_UNSET:
*(o->ovar) = o->odefault;
break;
case OPT_SET:
*(o->ovar) = ! o->odefault;
break;
}
}
break;
case TRIPLE:
@ -377,17 +395,20 @@ public void toggle_option(struct loption *o, int lower, char *s, int how_toggle)
* If user gave the upper case letter, then switch
* to 2 unless already 2, in which case make it 0.
*/
switch (how_toggle)
if (o->ovar != NULL)
{
case OPT_TOGGLE:
*(o->ovar) = flip_triple(*(o->ovar), lower);
break;
case OPT_UNSET:
*(o->ovar) = o->odefault;
break;
case OPT_SET:
*(o->ovar) = flip_triple(o->odefault, lower);
break;
switch (how_toggle)
{
case OPT_TOGGLE:
*(o->ovar) = flip_triple(*(o->ovar), lower);
break;
case OPT_UNSET:
*(o->ovar) = o->odefault;
break;
case OPT_SET:
*(o->ovar) = flip_triple(o->odefault, lower);
break;
}
}
break;
case STRING:
@ -411,7 +432,7 @@ public void toggle_option(struct loption *o, int lower, char *s, int how_toggle)
switch (how_toggle)
{
case OPT_TOGGLE:
num = getnum(&s, NULL, &err);
num = getnumc(&s, NULL, &err);
if (!err)
*(o->ovar) = num;
break;
@ -451,7 +472,8 @@ public void toggle_option(struct loption *o, int lower, char *s, int how_toggle)
/*
* Print the odesc message.
*/
error(o->odesc[*(o->ovar)], NULL_PARG);
if (o->ovar != NULL)
error(o->odesc[*(o->ovar)], NULL_PARG);
break;
case NUMBER:
/*
@ -470,7 +492,7 @@ public void toggle_option(struct loption *o, int lower, char *s, int how_toggle)
}
if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT))
screen_trashed = TRUE;
screen_trashed();
}
/*
@ -500,7 +522,7 @@ public int opt_has_param(struct loption *o)
* Return the prompt to be used for a given option letter.
* Only string and number valued options have prompts.
*/
public char * opt_prompt(struct loption *o)
public constant char * opt_prompt(struct loption *o)
{
if (o == NULL || (o->otype & (STRING|NUMBER)) == 0)
return ("?");
@ -511,7 +533,7 @@ public char * opt_prompt(struct loption *o)
* If the specified option can be toggled, return NULL.
* Otherwise return an appropriate error message.
*/
public char * opt_toggle_disallowed(int c)
public constant char * opt_toggle_disallowed(int c)
{
switch (c)
{
@ -530,7 +552,7 @@ public char * opt_toggle_disallowed(int c)
* In that case, the current option is taken to be the string for
* the previous option.
*/
public int isoptpending(void)
public lbool isoptpending(void)
{
return (pendopt != NULL);
}
@ -538,7 +560,7 @@ public int isoptpending(void)
/*
* Print error message about missing string.
*/
static void nostring(char *printopt)
static void nostring(constant char *printopt)
{
PARG parg;
parg.p_string = printopt;
@ -557,10 +579,16 @@ public void nopendopt(void)
* Scan to end of string or to an END_OPTION_STRING character.
* In the latter case, replace the char with a null char.
* Return a pointer to the remainder of the string, if any.
* validchars is of the form "[-][.]d[,]".
* "-" means an optional leading "-" is allowed
* "." means an optional leading "." is allowed (after any "-")
* "d" indicates a string of one or more digits (0-9)
* "," indicates a comma-separated list of digit strings is allowed
* "s" means a space char terminates the argument
*/
static char * optstring(char *s, char **p_str, char *printopt, char *validchars)
static constant char * optstring(constant char *s, char **p_str, constant char *printopt, constant char *validchars)
{
char *p;
constant char *p;
char *out;
if (*s == '\0')
@ -580,8 +608,38 @@ static char * optstring(char *s, char **p_str, char *printopt, char *validchars)
++p;
} else
{
if (*p == END_OPTION_STRING ||
(validchars != NULL && strchr(validchars, *p) == NULL))
if (validchars != NULL)
{
if (validchars[0] == 's')
{
if (*p == ' ')
break;
} else if (*p == '-')
{
if (validchars[0] != '-')
break;
++validchars;
} else if (*p == '.')
{
if (validchars[0] == '-')
++validchars;
if (validchars[0] != '.')
break;
++validchars;
} else if (*p == ',')
{
if (validchars[0] == '\0' || validchars[1] != ',')
break;
} else if (*p >= '0' && *p <= '9')
{
while (validchars[0] == '-' || validchars[0] == '.')
++validchars;
if (validchars[0] != 'd')
break;
} else
break;
}
if (*p == END_OPTION_STRING)
/* End of option string. */
break;
}
@ -593,7 +651,7 @@ static char * optstring(char *s, char **p_str, char *printopt, char *validchars)
/*
*/
static int num_error(char *printopt, int *errp, int overflow)
static int num_error(constant char *printopt, lbool *errp, lbool overflow)
{
PARG parg;
@ -618,13 +676,13 @@ static int num_error(char *printopt, int *errp, int overflow)
* Like atoi(), but takes a pointer to a char *, and updates
* the char * to point after the translated number.
*/
public int getnum(char **sp, char *printopt, int *errp)
public int getnumc(constant char **sp, constant char *printopt, lbool *errp)
{
char *s;
constant char *s = *sp;
int n;
int neg;
lbool neg;
s = skipsp(*sp);
s = skipspc(s);
neg = FALSE;
if (*s == '-')
{
@ -634,7 +692,7 @@ public int getnum(char **sp, char *printopt, int *errp)
if (*s < '0' || *s > '9')
return (num_error(printopt, errp, FALSE));
n = lstrtoi(s, sp, 10);
n = lstrtoic(s, sp, 10);
if (n < 0)
return (num_error(printopt, errp, TRUE));
if (errp != NULL)
@ -644,19 +702,27 @@ public int getnum(char **sp, char *printopt, int *errp)
return (n);
}
public int getnum(char **sp, constant char *printopt, lbool *errp)
{
constant char *cs = *sp;
int r = getnumc(&cs, printopt, errp);
*sp = (char *) cs;
return r;
}
/*
* Translate a string into a fraction, represented by the part of a
* number which would follow a decimal point.
* The value of the fraction is returned as parts per NUM_FRAC_DENOM.
* That is, if "n" is returned, the fraction intended is n/NUM_FRAC_DENOM.
*/
public long getfraction(char **sp, char *printopt, int *errp)
public long getfraction(constant char **sp, constant char *printopt, lbool *errp)
{
char *s;
constant char *s;
long frac = 0;
int fraclen = 0;
s = skipsp(*sp);
s = skipspc(*sp);
if (*s < '0' || *s > '9')
return (num_error(printopt, errp, FALSE));
@ -675,6 +741,34 @@ public long getfraction(char **sp, char *printopt, int *errp)
return (frac);
}
/*
* Set the UNSUPPORTED bit in every option listed
* in the LESS_UNSUPPORT environment variable.
*/
public void init_unsupport(void)
{
constant char *s = lgetenv("LESS_UNSUPPORT");
if (isnullenv(s))
return;
for (;;)
{
struct loption *opt;
s = skipspc(s);
if (*s == '\0') break;
if (*s == '-' && *++s == '\0') break;
if (*s == '-') /* long option name */
{
++s;
opt = findopt_name(&s, NULL, NULL);
} else /* short (single-char) option */
{
opt = findopt(*s);
if (opt != NULL) ++s;
}
if (opt != NULL)
opt->otype |= UNSUPPORTED;
}
}
/*
* Get the value of the -e flag.

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -23,6 +23,7 @@
#define HL_REPAINT 0200 /* Repaint hilites after toggling option */
#define NO_QUERY 0400 /* Option cannot be queried with "_" cmd */
#define INIT_HANDLER 01000 /* Call option handler function at startup */
#define UNSUPPORTED 02000 /* Option is unsupported via LESS_UNSUPPORT */
#define OTYPE (BOOL|TRIPLE|NUMBER|STRING|NOVAR)
@ -47,7 +48,7 @@
struct optname
{
char *oname; /* Long (GNU-style) option name */
constant char *oname; /* Long (GNU-style) option name */
struct optname *onext; /* List of synonymous option names */
};
@ -60,7 +61,7 @@ struct loption
int otype; /* Type of the option */
int odefault; /* Default value */
int *ovar; /* Pointer to the associated variable */
void (*ofunc)(int, char*); /* Pointer to special handling function */
char *odesc[3]; /* Description of each value */
void (*ofunc)(int, constant char*); /* Pointer to special handling function */
constant char *odesc[3]; /* Description of each value */
};

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -41,6 +41,7 @@ public int force_open; /* Open the file even if not regular file */
public int swindow; /* Size of scrolling window */
public int jump_sline; /* Screen line of "jump target" */
public long jump_sline_fraction = -1;
public int shift_count; /* Number of positions to shift horizontally */
public long shift_count_fraction = -1;
public int chopline; /* Truncate displayed lines at screen width */
public int wordwrap; /* Wrap lines at space */
@ -48,14 +49,13 @@ public int no_init; /* Disable sending ti/te termcap strings */
public int no_keypad; /* Disable sending ks/ke termcap strings */
public int twiddle; /* Show tildes after EOF */
public int show_attn; /* Hilite first unread line */
public int shift_count; /* Number of positions to shift horizontally */
public int status_col; /* Display a status column */
public int use_lessopen; /* Use the LESSOPEN filter */
public int quit_on_intr; /* Quit on interrupt */
public int follow_mode; /* F cmd Follows file desc or file name? */
public int oldbot; /* Old bottom of screen behavior {{REMOVE}} */
public int opt_use_backslash; /* Use backslash escaping in option parsing */
public char rscroll_char; /* Char which marks chopped lines with -S */
public LWCHAR rscroll_char; /* Char which marks chopped lines with -S */
public int rscroll_attr; /* Attribute of rscroll_char */
public int no_hist_dups; /* Remove dups from history list */
public int mousecap; /* Allow mouse for scrolling */
@ -70,7 +70,8 @@ public int status_line; /* Highlight entire marked lines */
public int header_lines; /* Freeze header lines at top of screen */
public int header_cols; /* Freeze header columns at left of screen */
public int nonum_headers; /* Don't give headers line numbers */
public int nosearch_headers; /* Don't search in header lines or columns */
public int nosearch_header_lines = 0; /* Don't search in header lines */
public int nosearch_header_cols = 0; /* Don't search in header columns */
public int redraw_on_quit; /* Redraw last screen after term deinit */
public int def_search_type; /* */
public int exit_F_on_close; /* Exit F command when input closes */
@ -79,6 +80,8 @@ public int show_preproc_error; /* Display msg when preproc exits with error */
public int proc_backspace; /* Special handling of backspace */
public int proc_tab; /* Special handling of tab */
public int proc_return; /* Special handling of carriage return */
public int match_shift; /* Extra horizontal shift on search match */
public long match_shift_fraction = NUM_FRAC_DENOM/2; /* 1/2 of screen width */
public char intr_char = CONTROL('X'); /* Char to interrupt reads */
#if HILITE_SEARCH
public int hilite_search; /* Highlight matched search patterns? */
@ -109,6 +112,7 @@ static struct optname J__optname = { "status-column", NULL };
static struct optname k_optname = { "lesskey-file", NULL };
#if HAVE_LESSKEYSRC
static struct optname ks_optname = { "lesskey-src", NULL };
static struct optname kc_optname = { "lesskey-content", NULL };
#endif /* HAVE_LESSKEYSRC */
#endif
static struct optname K__optname = { "quit-on-intr", NULL };
@ -159,6 +163,8 @@ static struct optname status_line_optname = { "status-line", NULL };
static struct optname header_optname = { "header", NULL };
static struct optname nonum_headers_optname = { "no-number-headers", NULL };
static struct optname nosearch_headers_optname = { "no-search-headers", NULL };
static struct optname nosearch_header_lines_optname = { "no-search-header-lines", NULL };
static struct optname nosearch_header_cols_optname = { "no-search-header-columns", NULL };
static struct optname redraw_on_quit_optname = { "redraw-on-quit", NULL };
static struct optname search_type_optname = { "search-options", NULL };
static struct optname exit_F_on_close_optname = { "exit-follow-on-close", NULL };
@ -170,6 +176,7 @@ static struct optname show_preproc_error_optname = { "show-preproc-errors", NULL
static struct optname proc_backspace_optname = { "proc-backspace", NULL };
static struct optname proc_tab_optname = { "proc-tab", NULL };
static struct optname proc_return_optname = { "proc-return", NULL };
static struct optname match_shift_optname = { "match-shift", NULL };
#if LESSTEST
static struct optname ttyin_name_optname = { "tty", NULL };
#endif /*LESSTEST*/
@ -232,11 +239,7 @@ static struct loption option[] =
},
{ 'D', &D__optname,
STRING|REPAINT|NO_QUERY, 0, NULL, opt_D,
{
"color desc: ",
NULL,
NULL
}
{ "color desc: ", "s", NULL }
},
{ 'e', &e_optname,
TRIPLE, OPT_OFF, &quit_at_eof, NULL,
@ -292,7 +295,7 @@ static struct loption option[] =
STRING, 0, NULL, opt_j,
{
"Target line: ",
"0123456789.-",
"-.d",
NULL
}
},
@ -310,6 +313,10 @@ static struct loption option[] =
{ NULL, NULL, NULL }
},
#if HAVE_LESSKEYSRC
{ OLETTER_NONE, &kc_optname,
STRING|NO_TOGGLE|NO_QUERY, 0, NULL, opt_kc,
{ NULL, NULL, NULL }
},
{ OLETTER_NONE, &ks_optname,
STRING|NO_TOGGLE|NO_QUERY, 0, NULL, opt_ks,
{ NULL, NULL, NULL }
@ -391,7 +398,7 @@ static struct loption option[] =
}
},
{ 'S', &S__optname,
BOOL|REPAINT, OPT_OFF, &chopline, NULL,
BOOL|REPAINT, OPT_OFF, &chopline, opt__S,
{
"Fold long lines",
"Chop long lines",
@ -432,7 +439,7 @@ static struct loption option[] =
STRING|REPAINT, 0, NULL, opt_x,
{
"Tab stops: ",
"0123456789,",
"d,",
NULL
}
},
@ -462,7 +469,7 @@ static struct loption option[] =
},
{ '"', &quote_optname,
STRING, 0, NULL, opt_quote,
{ "quotes: ", NULL, NULL }
{ "quotes: ", "s", NULL }
},
{ '~', &tilde_optname,
BOOL|REPAINT, OPT_ON, &twiddle, NULL,
@ -480,7 +487,7 @@ static struct loption option[] =
STRING, 0, NULL, opt_shift,
{
"Horizontal shift: ",
"0123456789.",
".d",
NULL
}
},
@ -518,7 +525,7 @@ static struct loption option[] =
},
{ OLETTER_NONE, &rscroll_optname,
STRING|REPAINT|INIT_HANDLER, 0, NULL, opt_rscroll,
{ "rscroll character: ", NULL, NULL }
{ "rscroll character: ", "s", NULL }
},
{ OLETTER_NONE, &nohistdups_optname,
BOOL, OPT_OFF, &no_hist_dups, NULL,
@ -602,11 +609,7 @@ static struct loption option[] =
},
{ OLETTER_NONE, &header_optname,
STRING|REPAINT, 0, NULL, opt_header,
{
"Header lines: ",
NULL,
NULL
}
{ "Header lines: ", "d,", NULL }
},
{ OLETTER_NONE, &nonum_headers_optname,
BOOL|REPAINT, 0, &nonum_headers, NULL,
@ -617,11 +620,21 @@ static struct loption option[] =
}
},
{ OLETTER_NONE, &nosearch_headers_optname,
BOOL|HL_REPAINT, 0, &nosearch_headers, NULL,
BOOL|HL_REPAINT, 0, NULL, opt_nosearch_headers,
{
"Search includes header lines",
"Search does not include header lines",
NULL
NULL, NULL, NULL
}
},
{ OLETTER_NONE, &nosearch_header_lines_optname,
BOOL|HL_REPAINT, 0, NULL, opt_nosearch_header_lines,
{
NULL, NULL, NULL
}
},
{ OLETTER_NONE, &nosearch_header_cols_optname,
BOOL|HL_REPAINT, 0, NULL, opt_nosearch_header_cols,
{
NULL, NULL, NULL
}
},
{ OLETTER_NONE, &redraw_on_quit_optname,
@ -634,11 +647,7 @@ static struct loption option[] =
},
{ OLETTER_NONE, &search_type_optname,
STRING, 0, NULL, opt_search_type,
{
"Search options: ",
NULL,
NULL
}
{ "Search options: ", "s", NULL }
},
{ OLETTER_NONE, &exit_F_on_close_optname,
BOOL, OPT_OFF, &exit_F_on_close, NULL,
@ -666,7 +675,7 @@ static struct loption option[] =
},
{ OLETTER_NONE, &intr_optname,
STRING, 0, NULL, opt_intr,
{ "interrupt character: ", NULL, NULL }
{ "interrupt character: ", "s", NULL }
},
{ OLETTER_NONE, &wordwrap_optname,
BOOL|REPAINT, OPT_OFF, &wordwrap, NULL,
@ -708,6 +717,14 @@ static struct loption option[] =
"Print carriage return as ^M"
}
},
{ OLETTER_NONE, &match_shift_optname,
STRING|INIT_HANDLER, 0, NULL, opt_match_shift,
{
"Search match shift: ",
".d",
NULL
}
},
#if LESSTEST
{ OLETTER_NONE, &ttyin_name_optname,
STRING|NO_TOGGLE, 0, NULL, opt_ttyin_name,
@ -728,7 +745,7 @@ static struct loption option[] =
public void init_option(void)
{
struct loption *o;
char *p;
constant char *p;
p = lgetenv("LESS_IS_MORE");
if (!isnullenv(p))
@ -766,15 +783,15 @@ public struct loption * findopt(int c)
/*
*
*/
static int is_optchar(char c)
static lbool is_optchar(char c)
{
if (ASCII_IS_UPPER(c))
return 1;
return TRUE;
if (ASCII_IS_LOWER(c))
return 1;
return TRUE;
if (c == '-')
return 1;
return 0;
return TRUE;
return FALSE;
}
/*
@ -783,18 +800,18 @@ static int is_optchar(char c)
* is updated to point after the matched name.
* p_oname if non-NULL is set to point to the full option name.
*/
public struct loption * findopt_name(char **p_optname, char **p_oname, int *p_err)
public struct loption * findopt_name(constant char **p_optname, constant char **p_oname, lbool *p_ambig)
{
char *optname = *p_optname;
constant char *optname = *p_optname;
struct loption *o;
struct optname *oname;
int len;
size_t len;
int uppercase;
struct loption *maxo = NULL;
struct optname *maxoname = NULL;
int maxlen = 0;
int ambig = 0;
int exact = 0;
size_t maxlen = 0;
lbool ambig = FALSE;
lbool exact = FALSE;
/*
* Check all options.
@ -814,7 +831,7 @@ public struct loption * findopt_name(char **p_optname, char **p_oname, int *p_er
for (uppercase = 0; uppercase <= 1; uppercase++)
{
len = sprefix(optname, oname->oname, uppercase);
if (len <= 0 || is_optchar(optname[len]))
if (len == 0 || is_optchar(optname[len]))
{
/*
* We didn't use all of the option name.
@ -827,7 +844,7 @@ public struct loption * findopt_name(char **p_optname, char **p_oname, int *p_er
* and now there's another one that
* matches the same length.
*/
ambig = 1;
ambig = TRUE;
else if (len > maxlen)
{
/*
@ -837,21 +854,21 @@ public struct loption * findopt_name(char **p_optname, char **p_oname, int *p_er
maxo = o;
maxoname = oname;
maxlen = len;
ambig = 0;
exact = (len == (int)strlen(oname->oname));
ambig = FALSE;
exact = (len == strlen(oname->oname));
}
if (!(o->otype & TRIPLE))
break;
}
}
}
if (p_ambig != NULL)
*p_ambig = ambig;
if (ambig)
{
/*
* Name matched more than one option.
*/
if (p_err != NULL)
*p_err = OPT_AMBIG;
return (NULL);
}
*p_optname = optname + maxlen;

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -42,13 +42,13 @@
#if HAVE_POLL && !MSDOS_COMPILER
#define USE_POLL 1
static int use_poll = TRUE;
static lbool use_poll = TRUE;
#else
#define USE_POLL 0
#endif
#if USE_POLL
#include <poll.h>
static int any_data = FALSE;
static lbool any_data = FALSE;
#endif
/*
@ -67,7 +67,7 @@ static int any_data = FALSE;
#endif
public int reading;
public int waiting_for_data;
public lbool waiting_for_data;
public int consecutive_nulls = 0;
/* Milliseconds to wait for data before displaying "waiting for data" message. */
@ -80,16 +80,14 @@ extern int exit_F_on_close;
extern int follow_mode;
extern int scanning_eof;
extern char intr_char;
extern int is_tty;
#if !MSDOS_COMPILER
extern int tty;
#endif
#if LESSTEST
extern char *ttyin_name;
#endif /*LESSTEST*/
public void init_poll(void)
{
char *delay = lgetenv("LESS_DATA_DELAY");
constant char *delay = lgetenv("LESS_DATA_DELAY");
int idelay = (delay == NULL) ? 0 : atoi(delay);
if (idelay > 0)
waiting_for_data_delay = idelay;
@ -125,16 +123,16 @@ static int check_poll(int fd, int tty)
}
poll(poller, 2, timeout);
#if LESSTEST
if (ttyin_name == NULL) /* Check for ^X only on a real tty. */
if (!is_lesstest()) /* Check for ^X only on a real tty. */
#endif /*LESSTEST*/
{
if (poller[1].revents & POLLIN)
{
LWCHAR ch = getchr();
if (ch == intr_char)
int ch = getchr();
if (ch < 0 || ch == intr_char)
/* Break out of "waiting for data". */
return (READ_INTR);
ungetcc_back(ch);
ungetcc_back((char) ch);
}
}
if (ignore_eoi && exit_F_on_close && (poller[0].revents & (POLLHUP|POLLIN)) == POLLHUP)
@ -150,11 +148,15 @@ static int check_poll(int fd, int tty)
public int supports_ctrl_x(void)
{
#if MSDOS_COMPILER==WIN32C
return (TRUE);
#else
#if USE_POLL
return (use_poll);
#else
return (FALSE);
#endif /* USE_POLL */
#endif /* MSDOS_COMPILER==WIN32C */
}
/*
@ -162,9 +164,9 @@ public int supports_ctrl_x(void)
* A call to intread() from a signal handler will interrupt
* any pending iread().
*/
public int iread(int fd, unsigned char *buf, unsigned int len)
public ssize_t iread(int fd, unsigned char *buf, size_t len)
{
int n;
ssize_t n;
start:
#if MSDOS_COMPILER==WIN32C
@ -188,7 +190,7 @@ start:
/*
* We jumped here from intread.
*/
reading = 0;
reading = FALSE;
#if HAVE_SIGPROCMASK
{
sigset_t mask;
@ -213,7 +215,7 @@ start:
}
flush();
reading = 1;
reading = TRUE;
#if MSDOS_COMPILER==DJGPPC
if (isatty(fd))
{
@ -230,20 +232,20 @@ start:
FD_SET(fd, &readfds);
if (select(fd+1, &readfds, 0, 0, 0) == -1)
{
reading = 0;
reading = FALSE;
return (READ_ERR);
}
}
#endif
#if USE_POLL
if (fd != tty && use_poll)
if (is_tty && fd != tty && use_poll)
{
int ret = check_poll(fd, tty);
if (ret != 0)
{
if (ret == READ_INTR)
sigs |= S_INTERRUPT;
reading = 0;
reading = FALSE;
return (ret);
}
}
@ -257,7 +259,7 @@ start:
if (c == intr_char)
{
sigs |= S_INTERRUPT;
reading = 0;
reading = FALSE;
return (READ_INTR);
}
WIN32ungetch(c);
@ -265,7 +267,7 @@ start:
#endif
#endif
n = read(fd, buf, len);
reading = 0;
reading = FALSE;
#if 1
/*
* This is a kludge to workaround a problem on some systems
@ -355,11 +357,11 @@ static char * strerror(int err)
/*
* errno_message: Return an error message based on the value of "errno".
*/
public char * errno_message(char *filename)
public char * errno_message(constant char *filename)
{
char *p;
char *m;
int len;
size_t len;
#if HAVE_ERRNO
#if MUST_DEFINE_ERRNO
extern int errno;
@ -368,7 +370,7 @@ public char * errno_message(char *filename)
#else
p = "cannot open";
#endif
len = (int) (strlen(filename) + strlen(p) + 3);
len = strlen(filename) + strlen(p) + 3;
m = (char *) ecalloc(len, sizeof(char));
SNPRINTF2(m, len, "%s: %s", filename, p);
return (m);
@ -378,11 +380,11 @@ public char * errno_message(char *filename)
* Return a description of a signal.
* The return value is good until the next call to this function.
*/
public char * signal_message(int sig)
public constant char * signal_message(int sig)
{
static char sigbuf[sizeof("Signal ") + INT_STRLEN_BOUND(sig) + 1];
#if HAVE_STRSIGNAL
char *description = strsignal(sig);
constant char *description = strsignal(sig);
if (description)
return description;
#endif
@ -395,7 +397,7 @@ public char * signal_message(int sig)
* and min(VAL, NUM) <= DEN so the result cannot overflow.
* Round to the nearest integer, breaking ties by rounding to even.
*/
public uintmax muldiv(uintmax val, uintmax num, uintmax den)
public uintmax umuldiv(uintmax val, uintmax num, uintmax den)
{
/*
* Like round(val * (double) num / den), but without rounding error.
@ -416,7 +418,7 @@ public uintmax muldiv(uintmax val, uintmax num, uintmax den)
*/
public int percentage(POSITION num, POSITION den)
{
return (int) muldiv(num, (POSITION) 100, den);
return (int) muldiv(num, 100, den);
}
/*
@ -433,7 +435,7 @@ public POSITION percent_pos(POSITION pos, int percent, long fraction)
*/
POSITION pctden = (percent * NUM_FRAC_DENOM) + fraction;
return (POSITION) muldiv(pos, pctden, 100 * (POSITION) NUM_FRAC_DENOM);
return (POSITION) muldiv(pos, pctden, 100 * NUM_FRAC_DENOM);
}
#if !HAVE_STRCHR
@ -452,7 +454,7 @@ char * strchr(char *s, char c)
#endif
#if !HAVE_MEMCPY
void * memcpy(void *dst, void *src, int len)
void * memcpy(void *dst, void *src, size_t len)
{
char *dstp = (char *) dst;
char *srcp = (char *) src;
@ -498,7 +500,7 @@ public void sleep_ms(int ms)
nanosleep(&t, NULL);
#else
#if HAVE_USLEEP
usleep(ms);
usleep(ms * 1000);
#else
sleep(ms / 1000 + (ms % 1000 != 0));
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -28,9 +28,9 @@ public int at_prompt;
extern int sigs;
extern int sc_width;
extern int so_s_width, so_e_width;
extern int screen_trashed;
extern int is_tty;
extern int oldbot;
extern int utf_mode;
extern char intr_char;
#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
@ -52,7 +52,7 @@ extern int vt_enabled;
public void put_line(void)
{
int c;
int i;
size_t i;
int a;
if (ABORT_SIGS())
@ -60,7 +60,7 @@ public void put_line(void)
/*
* Don't output if a signal is pending.
*/
screen_trashed = 1;
screen_trashed();
return;
}
@ -79,59 +79,222 @@ public void put_line(void)
at_exit();
}
/*
* win_flush has at least one non-critical issue when an escape sequence
* begins at the last char of the buffer, and possibly more issues.
* as a temporary measure to reduce likelyhood of encountering end-of-buffer
* issues till the SGR parser is replaced, OUTBUF_SIZE is 8K on Windows.
*/
static char obuf[OUTBUF_SIZE];
static char *ob = obuf;
static int outfd = 2; /* stderr */
#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
typedef unsigned t_attr;
#define A_BOLD (1u<<0)
#define A_ITALIC (1u<<1)
#define A_UNDERLINE (1u<<2)
#define A_BLINK (1u<<3)
#define A_INVERSE (1u<<4)
#define A_CONCEAL (1u<<5)
/* long is guaranteed 32 bits, and we reserve bits for type + RGB */
typedef unsigned long t_color;
#define T_DEFAULT 0ul
#define T_ANSI 1ul /* colors 0-7 */
#define CGET_ANSI(c) ((c) & 0x7)
#define C_DEFAULT (T_DEFAULT <<24) /* 0 */
#define C_ANSI(c) ((T_ANSI <<24) | (c))
/* attr/fg/bg/all 0 is the default attr/fg/bg/all, respectively */
typedef struct t_sgr {
t_attr attr;
t_color fg;
t_color bg;
} t_sgr;
static constant t_sgr SGR_DEFAULT; /* = {0} */
/* returns 0 on success, non-0 on unknown SGR code */
static int update_sgr(t_sgr *sgr, long code)
{
switch (code)
{
case 0: *sgr = SGR_DEFAULT; break;
case 1: sgr->attr |= A_BOLD; break;
case 22: sgr->attr &= ~A_BOLD; break;
case 3: sgr->attr |= A_ITALIC; break;
case 23: sgr->attr &= ~A_ITALIC; break;
case 4: sgr->attr |= A_UNDERLINE; break;
case 24: sgr->attr &= ~A_UNDERLINE; break;
case 6: /* fast-blink, fallthrough */
case 5: sgr->attr |= A_BLINK; break;
case 25: sgr->attr &= ~A_BLINK; break;
case 7: sgr->attr |= A_INVERSE; break;
case 27: sgr->attr &= ~A_INVERSE; break;
case 8: sgr->attr |= A_CONCEAL; break;
case 28: sgr->attr &= ~A_CONCEAL; break;
case 39: sgr->fg = C_DEFAULT; break;
case 49: sgr->bg = C_DEFAULT; break;
case 30: case 31: case 32: case 33:
case 34: case 35: case 36: case 37:
sgr->fg = C_ANSI(code - 30);
break;
case 40: case 41: case 42: case 43:
case 44: case 45: case 46: case 47:
sgr->bg = C_ANSI(code - 40);
break;
default:
return 1;
}
return 0;
}
static void set_win_colors(t_sgr *sgr)
{
#if MSDOS_COMPILER==WIN32C
/* Screen colors used by 3x and 4x SGR commands. */
static unsigned char screen_color[] = {
0, /* BLACK */
FOREGROUND_RED,
FOREGROUND_GREEN,
FOREGROUND_RED|FOREGROUND_GREEN,
FOREGROUND_BLUE,
FOREGROUND_BLUE|FOREGROUND_RED,
FOREGROUND_BLUE|FOREGROUND_GREEN,
FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED
};
#else
static enum COLORS screen_color[] = {
BLACK, RED, GREEN, BROWN,
BLUE, MAGENTA, CYAN, LIGHTGRAY
};
#endif
int fg, bg, tmp; /* Windows colors */
/* Not "SGR mode": apply -D<x> to default fg+bg with one attribute */
if (!sgr_mode && sgr->fg == C_DEFAULT && sgr->bg == C_DEFAULT)
{
switch (sgr->attr)
{
case A_BOLD:
WIN32setcolors(bo_fg_color, bo_bg_color);
return;
case A_UNDERLINE:
WIN32setcolors(ul_fg_color, ul_bg_color);
return;
case A_BLINK:
WIN32setcolors(bl_fg_color, bl_bg_color);
return;
case A_INVERSE:
WIN32setcolors(so_fg_color, so_bg_color);
return;
/*
* There's no -Di so italic should not be here, but to
* preserve legacy behavior, apply -Ds to italic too.
*/
case A_ITALIC:
WIN32setcolors(so_fg_color, so_bg_color);
return;
}
}
/* generic application of the SGR state as Windows colors */
fg = sgr->fg == C_DEFAULT ? nm_fg_color
: screen_color[CGET_ANSI(sgr->fg)];
bg = sgr->bg == C_DEFAULT ? nm_bg_color
: screen_color[CGET_ANSI(sgr->bg)];
if (sgr->attr & A_BOLD)
fg |= 8;
if (sgr->attr & (A_BLINK | A_UNDERLINE))
bg |= 8; /* TODO: can be illegible */
if (sgr->attr & (A_INVERSE | A_ITALIC))
{
tmp = fg;
fg = bg;
bg = tmp;
}
if (sgr->attr & A_CONCEAL)
fg = bg ^ 8;
WIN32setcolors(fg, bg);
}
/* like is_ansi_end, but doesn't assume c != 0 (returns 0 for c == 0) */
static int is_ansi_end_0(char c)
{
return c && is_ansi_end((unsigned char)c);
}
static void win_flush(void)
{
if (ctldisp != OPT_ONPLUS || (vt_enabled && sgr_mode))
WIN32textout(obuf, ob - obuf);
if (ctldisp != OPT_ONPLUS
#if MSDOS_COMPILER==WIN32C
|| (vt_enabled && sgr_mode)
#endif
)
WIN32textout(obuf, ptr_diff(ob, obuf));
else
{
/*
* Look for SGR escape sequences, and convert them
* to color commands. Replace bold, underline,
* and italic escapes into colors specified via
* the -D command-line option.
* Digest text, apply embedded SGR sequences as Windows-colors.
* By default - when -Da ("SGR mode") is unset - also apply
* translation of -D command-line options (at set_win_colors)
*/
char *anchor, *p, *p_next;
static int fg, fgi, bg, bgi;
static int at;
int f, b;
#if MSDOS_COMPILER==WIN32C
/* Screen colors used by 3x and 4x SGR commands. */
static unsigned char screen_color[] = {
0, /* BLACK */
FOREGROUND_RED,
FOREGROUND_GREEN,
FOREGROUND_RED|FOREGROUND_GREEN,
FOREGROUND_BLUE,
FOREGROUND_BLUE|FOREGROUND_RED,
FOREGROUND_BLUE|FOREGROUND_GREEN,
FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED
};
#else
static enum COLORS screen_color[] = {
BLACK, RED, GREEN, BROWN,
BLUE, MAGENTA, CYAN, LIGHTGRAY
};
#endif
static t_sgr sgr;
/* when unsupported SGR value is encountered, like 38/48 for
* 256/true colors, then we abort processing this sequence,
* because it may expect followup values, but we don't know
* how many, so we've lost sync of this sequence parsing.
* Without VT enabled it's OK because we can't do much anyway,
* but with VT enabled we choose to passthrough this sequence
* to the terminal - which can handle it better than us.
* however, this means that our "sgr" var is no longer in sync
* with the actual terminal state, which can lead to broken
* colors with future sequences which we _can_ fully parse.
* in such case, once it happens, we keep passthrough sequences
* until we know we're in sync again - on a valid reset.
*/
static int sgr_bad_sync;
if (fg == 0 && bg == 0)
{
fg = nm_fg_color & 7;
fgi = nm_fg_color & 8;
bg = nm_bg_color & 7;
bgi = nm_bg_color & 8;
}
for (anchor = p_next = obuf;
(p_next = memchr(p_next, ESC, ob - p_next)) != NULL; )
{
p = p_next;
if (p[1] == '[') /* "ESC-[" sequence */
{
/*
* unknown SGR code ignores the rest of the seq,
* and allows ignoring sequences such as
* ^[[38;5;123m or ^[[38;2;5;6;7m
* (prior known codes at the same seq do apply)
*/
int bad_code = 0;
if (p > anchor)
{
/*
@ -139,35 +302,30 @@ static void win_flush(void)
* the last escape sequence,
* write them out to the screen.
*/
WIN32textout(anchor, p-anchor);
WIN32textout(anchor, ptr_diff(p, anchor));
anchor = p;
}
p += 2; /* Skip the "ESC-[" */
if (is_ansi_end(*p))
if (is_ansi_end_0(*p))
{
/*
* Handle null escape sequence
* "ESC[m", which restores
* the normal color.
* "ESC[m" as if it was "ESC[0m"
*/
p++;
anchor = p_next = p;
fg = nm_fg_color & 7;
fgi = nm_fg_color & 8;
bg = nm_bg_color & 7;
bgi = nm_bg_color & 8;
at = 0;
WIN32setcolors(nm_fg_color, nm_bg_color);
update_sgr(&sgr, 0);
set_win_colors(&sgr);
sgr_bad_sync = 0;
continue;
}
p_next = p;
at &= ~32;
/*
* Select foreground/background colors
* Parse and apply SGR values to the SGR state
* based on the escape sequence.
*/
while (!is_ansi_end(*p))
while (!is_ansi_end_0(*p))
{
char *q;
long code = strtol(p, &q, 10);
@ -179,166 +337,54 @@ static void win_flush(void)
* Leave it unprocessed
* in the buffer.
*/
int slop = (int) (q - anchor);
/* {{ strcpy args overlap! }} */
strcpy(obuf, anchor);
size_t slop = ptr_diff(q, anchor);
memmove(obuf, anchor, slop);
ob = &obuf[slop];
return;
}
if (q == p ||
code > 49 || code < 0 ||
(!is_ansi_end(*q) && *q != ';'))
(!is_ansi_end_0(*q) && *q != ';'))
{
/*
* can't parse. passthrough
* till the end of the buffer
*/
p_next = q;
break;
}
if (*q == ';')
{
q++;
at |= 32;
}
switch (code)
{
default:
/* case 0: all attrs off */
fg = nm_fg_color & 7;
bg = nm_bg_color & 7;
at &= 32;
/*
* \e[0m use normal
* intensities, but
* \e[0;...m resets them
*/
if (at & 32)
{
fgi = 0;
bgi = 0;
} else
{
fgi = nm_fg_color & 8;
bgi = nm_bg_color & 8;
}
break;
case 1: /* bold on */
fgi = 8;
at |= 1;
break;
case 3: /* italic on */
case 7: /* inverse on */
at |= 2;
break;
case 4: /* underline on */
bgi = 8;
at |= 4;
break;
case 5: /* slow blink on */
case 6: /* fast blink on */
bgi = 8;
at |= 8;
break;
case 8: /* concealed on */
at |= 16;
break;
case 22: /* bold off */
fgi = 0;
at &= ~1;
break;
case 23: /* italic off */
case 27: /* inverse off */
at &= ~2;
break;
case 24: /* underline off */
bgi = 0;
at &= ~4;
break;
case 28: /* concealed off */
at &= ~16;
break;
case 30: case 31: case 32:
case 33: case 34: case 35:
case 36: case 37:
fg = screen_color[code - 30];
at |= 32;
break;
case 39: /* default fg */
fg = nm_fg_color & 7;
at |= 32;
break;
case 40: case 41: case 42:
case 43: case 44: case 45:
case 46: case 47:
bg = screen_color[code - 40];
at |= 32;
break;
case 49: /* default bg */
bg = nm_bg_color & 7;
at |= 32;
break;
}
if (!bad_code)
bad_code = update_sgr(&sgr, code);
if (bad_code)
sgr_bad_sync = 1;
else if (code == 0)
sgr_bad_sync = 0;
p = q;
}
if (!is_ansi_end(*p) || p == p_next)
if (!is_ansi_end_0(*p) || p == p_next)
break;
/*
* In SGR mode, the ANSI sequence is
* always honored; otherwise if an attr
* is used by itself ("\e[1m" versus
* "\e[1;33m", for example), set the
* color assigned to that attribute.
*/
if (sgr_mode || (at & 32))
{
if (at & 2)
{
f = bg | bgi;
b = fg | fgi;
} else
{
f = fg | fgi;
b = bg | bgi;
}
} else
{
if (at & 1)
{
f = bo_fg_color;
b = bo_bg_color;
} else if (at & 2)
{
f = so_fg_color;
b = so_bg_color;
} else if (at & 4)
{
f = ul_fg_color;
b = ul_bg_color;
} else if (at & 8)
{
f = bl_fg_color;
b = bl_bg_color;
} else
{
f = nm_fg_color;
b = nm_bg_color;
}
if (sgr_bad_sync && vt_enabled) {
/* this or a prior sequence had unknown
* SGR value. passthrough all sequences
* until we're in-sync again
*/
WIN32textout(anchor, ptr_diff(p+1, anchor));
} else {
set_win_colors(&sgr);
}
if (at & 16)
f = b ^ 8;
#if MSDOS_COMPILER==WIN32C
f &= 0xf | COMMON_LVB_UNDERSCORE;
#else
f &= 0xf;
#endif
b &= 0xf;
WIN32setcolors(f, b);
p_next = anchor = p + 1;
} else
p_next++;
}
/* Output what's left in the buffer. */
WIN32textout(anchor, ob - anchor);
WIN32textout(anchor, ptr_diff(ob, anchor));
}
ob = obuf;
}
@ -362,9 +408,9 @@ static void win_flush(void)
*/
public void flush(void)
{
int n;
size_t n;
n = (int) (ob - obuf);
n = ptr_diff(ob, obuf);
if (n == 0)
return;
ob = obuf;
@ -389,7 +435,7 @@ public void flush(void)
#endif
if (write(outfd, obuf, n) != n)
screen_trashed = 1;
screen_trashed();
}
/*
@ -403,9 +449,11 @@ public void set_output(int fd)
/*
* Output a character.
* ch is int for compatibility with tputs.
*/
public int putchr(int c)
public int putchr(int ch)
{
char c = (char) ch;
#if 0 /* fake UTF-8 output for testing */
extern int utf_mode;
if (utf_mode)
@ -492,25 +540,32 @@ TYPE_TO_A_FUNC(inttoa, int)
/*
* Convert a string to an integral type. Return ((type) -1) on overflow.
*/
#define STR_TO_TYPE_FUNC(funcname, type) \
type funcname(char *buf, char **ebuf, int radix) \
#define STR_TO_TYPE_FUNC(funcname, cfuncname, type) \
type cfuncname(constant char *buf, constant char **ebuf, int radix) \
{ \
type val = 0; \
int v = 0; \
lbool v = 0; \
for (;; buf++) { \
char c = *buf; \
int digit = (c >= '0' && c <= '9') ? c - '0' : (c >= 'a' && c <= 'f') ? c - 'a' + 10 : (c >= 'A' && c <= 'F') ? c - 'A' + 10 : -1; \
if (digit < 0 || digit >= radix) break; \
v |= ckd_mul(&val, val, radix); \
v |= ckd_add(&val, val, digit); \
v = v || ckd_mul(&val, val, radix); \
v = v || ckd_add(&val, val, digit); \
} \
if (ebuf != NULL) *ebuf = buf; \
return v ? -1 : val; \
return v ? (type)(-1) : val; \
} \
type funcname(char *buf, char **ebuf, int radix) \
{ \
constant char *cbuf = buf; \
type r = cfuncname(cbuf, &cbuf, radix); \
if (ebuf != NULL) *ebuf = (char *) cbuf; /*{{const-issue}}*/ \
return r; \
}
STR_TO_TYPE_FUNC(lstrtopos, POSITION)
STR_TO_TYPE_FUNC(lstrtoi, int)
STR_TO_TYPE_FUNC(lstrtoul, unsigned long)
STR_TO_TYPE_FUNC(lstrtopos, lstrtoposc, POSITION)
STR_TO_TYPE_FUNC(lstrtoi, lstrtoic, int)
STR_TO_TYPE_FUNC(lstrtoul, lstrtoulc, unsigned long)
/*
* Print an integral type.
@ -534,9 +589,10 @@ IPRINT_FUNC(iprint_linenum, LINENUM, linenumtoa)
* {{ This paranoia about the portability of printf dates from experiences
* with systems in the 1980s and is of course no longer necessary. }}
*/
public int less_printf(char *fmt, PARG *parg)
public int less_printf(constant char *fmt, PARG *parg)
{
char *s;
constant char *s;
constant char *es;
int col;
col = 0;
@ -553,11 +609,17 @@ public int less_printf(char *fmt, PARG *parg)
{
case 's':
s = parg->p_string;
es = s + strlen(s);
parg++;
while (*s != '\0')
{
putchr(*s++);
col++;
LWCHAR ch = step_charc(&s, +1, es);
constant char *ps = utf_mode ? prutfchar(ch) : prchar(ch);
while (*ps != '\0')
{
putchr(*ps++);
col++;
}
}
break;
case 'd':
@ -573,7 +635,7 @@ public int less_printf(char *fmt, PARG *parg)
parg++;
break;
case 'c':
s = prchar(parg->p_char);
s = prchar((LWCHAR) parg->p_char);
parg++;
while (*s != '\0')
{
@ -605,7 +667,7 @@ public void get_return(void)
#else
c = getchr();
if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
ungetcc(c);
ungetcc((char) c);
#endif
}
@ -613,7 +675,7 @@ public void get_return(void)
* Output a message in the lower left corner of the screen
* and wait for carriage return.
*/
public void error(char *fmt, PARG *parg)
public void error(constant char *fmt, PARG *parg)
{
int col = 0;
static char return_to_continue[] = " (press RETURN)";
@ -636,7 +698,7 @@ public void error(char *fmt, PARG *parg)
col += less_printf(fmt, parg);
putstr(return_to_continue);
at_exit();
col += sizeof(return_to_continue) + so_e_width;
col += (int) sizeof(return_to_continue) + so_e_width;
get_return();
lower_left();
@ -648,7 +710,7 @@ public void error(char *fmt, PARG *parg)
* {{ Unless the terminal doesn't have auto margins,
* in which case we just hammered on the right margin. }}
*/
screen_trashed = 1;
screen_trashed();
flush();
}
@ -659,7 +721,7 @@ public void error(char *fmt, PARG *parg)
* Usually used to warn that we are beginning a potentially
* time-consuming operation.
*/
static void ierror_suffix(char *fmt, PARG *parg, char *suffix1, char *suffix2, char *suffix3)
static void ierror_suffix(constant char *fmt, PARG *parg, constant char *suffix1, constant char *suffix2, constant char *suffix3)
{
at_exit();
clear_bot();
@ -673,25 +735,28 @@ static void ierror_suffix(char *fmt, PARG *parg, char *suffix1, char *suffix2, c
need_clr = 1;
}
public void ierror(char *fmt, PARG *parg)
public void ierror(constant char *fmt, PARG *parg)
{
ierror_suffix(fmt, parg, "... (interrupt to abort)", "", "");
}
public void ixerror(char *fmt, PARG *parg)
public void ixerror(constant char *fmt, PARG *parg)
{
if (!supports_ctrl_x())
ierror(fmt, parg);
else
ierror_suffix(fmt, parg,
"... (", prchar(intr_char), " or interrupt to abort)");
{
char ichar[MAX_PRCHAR_LEN+1];
strcpy(ichar, prchar((LWCHAR) intr_char));
ierror_suffix(fmt, parg, "... (", ichar, " or interrupt to abort)");
}
}
/*
* Output a message in the lower left corner of the screen
* and return a single-character response.
*/
public int query(char *fmt, PARG *parg)
public int query(constant char *fmt, PARG *parg)
{
int c;
int col = 0;
@ -706,7 +771,7 @@ public int query(char *fmt, PARG *parg)
{
lower_left();
if (col >= sc_width)
screen_trashed = 1;
screen_trashed();
flush();
} else
{

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -20,7 +20,7 @@ extern int utf_mode;
/*
* Compile a search pattern, for future use by match_pattern.
*/
static int compile_pattern2(char *pattern, int search_type, PATTERN_TYPE *comp_pattern, int show_error)
static int compile_pattern2(constant char *pattern, int search_type, PATTERN_TYPE *comp_pattern, int show_error)
{
if (search_type & SRCH_NO_REGEX)
return (0);
@ -142,21 +142,20 @@ static int compile_pattern2(char *pattern, int search_type, PATTERN_TYPE *comp_p
/*
* Like compile_pattern2, but convert the pattern to lowercase if necessary.
*/
public int compile_pattern(char *pattern, int search_type, int show_error, PATTERN_TYPE *comp_pattern)
public int compile_pattern(constant char *pattern, int search_type, int show_error, PATTERN_TYPE *comp_pattern)
{
char *cvt_pattern;
int result;
if (caseless != OPT_ONPLUS || (re_handles_caseless && !(search_type & SRCH_NO_REGEX)))
cvt_pattern = pattern;
else
{
cvt_pattern = (char*) ecalloc(1, cvt_length(strlen(pattern), CVT_TO_LC));
cvt_text(cvt_pattern, pattern, (int *)NULL, (int *)NULL, CVT_TO_LC);
}
result = compile_pattern2(cvt_pattern, search_type, comp_pattern, show_error);
if (cvt_pattern != pattern)
result = compile_pattern2(pattern, search_type, comp_pattern, show_error);
} else
{
char *cvt_pattern = (char*) ecalloc(1, cvt_length(strlen(pattern), CVT_TO_LC));
cvt_text(cvt_pattern, pattern, NULL, NULL, CVT_TO_LC);
result = compile_pattern2(cvt_pattern, search_type, comp_pattern, show_error);
free(cvt_pattern);
}
return (result);
}
@ -227,7 +226,7 @@ public int valid_pattern(char *pattern)
/*
* Is a compiled pattern null?
*/
public int is_null_pattern(PATTERN_TYPE pattern)
public lbool is_null_pattern(PATTERN_TYPE pattern)
{
#if HAVE_GNU_REGEX
return (pattern == NULL);
@ -258,12 +257,14 @@ public int is_null_pattern(PATTERN_TYPE pattern)
* Simple pattern matching function.
* It supports no metacharacters like *, etc.
*/
static int match(char *pattern, int pattern_len, char *buf, int buf_len, char ***sp, char ***ep, int nsubs)
static int match(constant char *pattern, size_t pattern_len, constant char *buf, int buf_len, constant char ***sp, constant char ***ep, int nsubs)
{
char *pp, *lp;
char *pattern_end = pattern + pattern_len;
char *buf_end = buf + buf_len;
constant char *pp;
constant char *lp;
constant char *pattern_end = pattern + pattern_len;
constant char *buf_end = buf + buf_len;
(void) nsubs;
for ( ; buf < buf_end; buf++)
{
for (pp = pattern, lp = buf; ; pp++, lp++)
@ -294,9 +295,10 @@ static int match(char *pattern, int pattern_len, char *buf, int buf_len, char **
* Set sp[i] and ep[i] to the start and end of the i-th matched subpattern.
* Subpatterns are defined by parentheses in the regex language.
*/
static int match_pattern1(PATTERN_TYPE pattern, char *tpattern, char *line, int line_len, char **sp, char **ep, int nsp, int notbol, int search_type)
static int match_pattern1(PATTERN_TYPE pattern, constant char *tpattern, constant char *line, size_t aline_len, constant char **sp, constant char **ep, int nsp, int notbol, int search_type)
{
int matched;
int line_len = (int) aline_len; /*{{type-issue}}*/
#if NO_REGEX
search_type |= SRCH_NO_REGEX;
@ -442,7 +444,7 @@ static int match_pattern1(PATTERN_TYPE pattern, char *tpattern, char *line, int
return (matched);
}
public int match_pattern(PATTERN_TYPE pattern, char *tpattern, char *line, int line_len, char **sp, char **ep, int nsp, int notbol, int search_type)
public int match_pattern(PATTERN_TYPE pattern, constant char *tpattern, constant char *line, size_t line_len, constant char **sp, constant char **ep, int nsp, int notbol, int search_type)
{
int matched = match_pattern1(pattern, tpattern, line, line_len, sp, ep, nsp, notbol, search_type);
int i;
@ -457,7 +459,7 @@ public int match_pattern(PATTERN_TYPE pattern, char *tpattern, char *line, int l
/*
* Return the name of the pattern matching library.
*/
public char * pattern_lib_name(void)
public constant char * pattern_lib_name(void)
{
#if HAVE_GNU_REGEX
return ("GNU");

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -47,8 +47,8 @@ extern int less_is_more;
/* ---- RE_COMP ---- */
#if HAVE_RE_COMP
char *re_comp(char*);
int re_exec(char*);
constant char *re_comp(constant char*);
int re_exec(constant char*);
#define PATTERN_TYPE int
#define SET_NULL_PATTERN(name) name = 0
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -25,7 +25,7 @@ static POSITION *table = NULL; /* The position table */
static int table_size = 0;
extern int sc_width, sc_height;
extern int header_lines;
extern int hshift;
/*
* Return the starting file position of a line displayed on the screen.
@ -113,7 +113,7 @@ public void pos_init(void)
free((char*)table);
} else
scrpos.pos = NULL_POSITION;
table = (POSITION *) ecalloc(sc_height, sizeof(POSITION));
table = (POSITION *) ecalloc((size_t) sc_height, sizeof(POSITION)); /*{{type-issue}}*/
table_size = sc_height;
pos_clear();
if (scrpos.pos != NULL_POSITION)
@ -236,3 +236,67 @@ public int sindex_from_sline(int sline)
*/
return (sline-1);
}
/*
* Given a line that starts at linepos,
* and the character at byte offset choff into that line,
* return the number of characters (not bytes) between the
* beginning of the line and the first byte of the choff character.
*/
static int pos_shift(POSITION linepos, size_t choff)
{
constant char *line;
size_t line_len;
POSITION pos;
int cvt_ops;
char *cline;
pos = forw_raw_line_len(linepos, choff, &line, &line_len);
if (pos == NULL_POSITION || line_len != choff)
return -1;
cvt_ops = get_cvt_ops(0); /* {{ Passing 0 ignores SRCH_NO_REGEX; does it matter? }} */
/* {{ It would be nice to be able to call cvt_text with dst=NULL, to avoid need to alloc a useless cline. }} */
cline = (char *) ecalloc(1, cvt_length(line_len, cvt_ops));
cvt_text(cline, line, NULL, &line_len, cvt_ops);
free(cline);
return (int) line_len; /*{{type-issue}}*/
}
/*
* Return the position of the first char of the line containing tpos.
* Thus if tpos is the first char of its line, just return tpos.
*/
static POSITION beginning_of_line(POSITION tpos)
{
ch_seek(tpos);
while (ch_tell() != ch_zero())
{
int ch = ch_back_get();
if (ch == '\n')
{
(void) ch_forw_get();
break;
}
}
return ch_tell();
}
/*
* When viewing long lines, it may be that the first char in the top screen
* line is not the first char in its (file) line (the table is "beheaded").
* This function sets that entry to the position of the first char in the line,
* and sets hshift so that the first char in the first line is unchanged.
*/
public void pos_rehead(void)
{
POSITION linepos;
POSITION tpos = table[TOP];
if (tpos == NULL_POSITION)
return;
linepos = beginning_of_line(tpos);
if (linepos == tpos)
return;
table[TOP] = linepos;
hshift = pos_shift(linepos, (size_t) (tpos - linepos));
screen_trashed();
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -21,19 +21,21 @@
#include "position.h"
extern int pr_type;
extern int new_file;
extern int sc_width;
extern int so_s_width, so_e_width;
extern lbool new_file;
extern int linenums;
extern int hshift;
extern int sc_height;
extern int jump_sline;
extern int less_is_more;
extern int header_lines;
extern int utf_mode;
extern IFILE curr_ifile;
#if OSC8_LINK
extern char *osc8_path;
#endif
#if EDITOR
extern char *editor;
extern char *editproto;
extern constant char *editor;
extern constant char *editproto;
#endif
/*
@ -78,29 +80,52 @@ public void init_prompt(void)
/*
* Append a string to the end of the message.
* nprt means the character *may* be nonprintable
* and should be converted to printable form.
*/
static void ap_str(char *s)
static void ap_estr(constant char *s, lbool nprt)
{
int len;
constant char *es = s + strlen(s);
while (*s != '\0')
{
LWCHAR ch = step_charc(&s, +1, es);
constant char *ps;
char ubuf[MAX_UTF_CHAR_LEN+1];
size_t plen;
len = (int) strlen(s);
if (mp + len >= message + PROMPT_SIZE)
len = (int) (message + PROMPT_SIZE - mp - 1);
strncpy(mp, s, len);
mp += len;
if (nprt)
{
ps = utf_mode ? prutfchar(ch) : prchar(ch);
} else
{
char *up = ubuf;
put_wchar(&up, ch);
*up = '\0';
ps = ubuf;
}
plen = strlen(ps);
if (mp + plen >= message + PROMPT_SIZE)
break;
strcpy(mp, ps);
mp += plen;
}
*mp = '\0';
}
static void ap_str(constant char *s)
{
ap_estr(s, FALSE);
}
/*
* Append a character to the end of the message.
*/
static void ap_char(char c)
{
char buf[2];
buf[0] = c;
buf[1] = '\0';
ap_str(buf);
if (mp + 1 >= message + PROMPT_SIZE)
return;
*mp++ = c;
*mp = '\0';
}
/*
@ -165,7 +190,7 @@ static POSITION curr_byte(int where)
* question mark followed by a single letter.
* Here we decode that letter and return the appropriate boolean value.
*/
static int cond(char c, int where)
static lbool cond(char c, int where)
{
POSITION len;
@ -185,7 +210,7 @@ static int cond(char c, int where)
case 'l': /* Line number known? */
case 'd': /* Same as l */
if (!linenums)
return 0;
return FALSE;
return (currline(where) != 0);
case 'L': /* Final line number known? */
case 'D': /* Final page number known? */
@ -198,13 +223,12 @@ static int cond(char c, int where)
#endif
case 'n': /* First prompt in a new file? */
#if TAGS
return (ntags() ? 1 : new_file);
return (ntags() ? TRUE : new_file ? TRUE : FALSE);
#else
return (new_file);
return (new_file ? TRUE : FALSE);
#endif
case 'p': /* Percent into file (bytes) known? */
return (curr_byte(where) != NULL_POSITION &&
ch_length() > 0);
return (curr_byte(where) != NULL_POSITION && ch_length() > 0);
case 'P': /* Percent into file (lines) known? */
return (currline(where) != 0 &&
(len = ch_length()) > 0 &&
@ -215,11 +239,11 @@ static int cond(char c, int where)
case 'x': /* Is there a "next" file? */
#if TAGS
if (ntags())
return (0);
return (FALSE);
#endif
return (next_ifile(curr_ifile) != NULL_IFILE);
}
return (0);
return (FALSE);
}
/*
@ -229,7 +253,7 @@ static int cond(char c, int where)
* Here we decode that letter and take the appropriate action,
* usually by appending something to the message being built.
*/
static void protochar(int c, int where, int iseditproto)
static void protochar(char c, int where)
{
POSITION pos;
POSITION len;
@ -284,10 +308,10 @@ static void protochar(int c, int where, int iseditproto)
break;
#endif
case 'f': /* File name */
ap_str(get_filename(curr_ifile));
ap_estr(get_filename(curr_ifile), TRUE);
break;
case 'F': /* Last component of file name */
ap_str(last_component(get_filename(curr_ifile)));
ap_estr(last_component(get_filename(curr_ifile)), TRUE);
break;
case 'g': /* Shell-escaped file name */
s = shell_quote(get_filename(curr_ifile));
@ -326,6 +350,14 @@ static void protochar(int c, int where, int iseditproto)
#endif
ap_int(nifile());
break;
case 'o': /* path (URI without protocol) of selected OSC8 link */
#if OSC8_LINK
if (osc8_path != NULL)
ap_str(osc8_path);
else
#endif
ap_quest();
break;
case 'p': /* Percent into file (bytes) */
pos = curr_byte(where);
len = ch_length();
@ -459,10 +491,10 @@ static constant char * wherechar(char constant *p, int *wp)
/*
* Construct a message based on a prototype string.
*/
public char * pr_expand(constant char *proto)
public constant char * pr_expand(constant char *proto)
{
constant char *p;
int c;
char c;
int where;
mp = message;
@ -504,13 +536,7 @@ public char * pr_expand(constant char *proto)
{
where = 0;
p = wherechar(p, &where);
protochar(c, where,
#if EDITOR
(proto == editproto));
#else
0);
#endif
protochar(c, where);
}
break;
}
@ -524,7 +550,7 @@ public char * pr_expand(constant char *proto)
/*
* Return a message suitable for printing by the "=" command.
*/
public char * eq_message(void)
public constant char * eq_message(void)
{
return (pr_expand(eqproto));
}
@ -535,22 +561,22 @@ public char * eq_message(void)
* If we can't come up with an appropriate prompt, return NULL
* and the caller will prompt with a colon.
*/
public char * pr_string(void)
public constant char * pr_string(void)
{
char *prompt;
constant char *prompt;
int type;
type = (!less_is_more) ? pr_type : pr_type ? 0 : 1;
prompt = pr_expand((ch_getflags() & CH_HELPFILE) ?
hproto : prproto[type]);
new_file = 0;
new_file = FALSE;
return (prompt);
}
/*
* Return a message suitable for printing while waiting in the F command.
*/
public char * wait_message(void)
public constant char * wait_message(void)
{
return (pr_expand(wproto));
}

View file

@ -158,7 +158,7 @@
/*
* Global work variables for regcomp().
*/
static char *regparse; /* Input-scan pointer. */
static constant char *regparse; /* Input-scan pointer. */
static int regnpar; /* () count. */
static char regdummy;
static char *regcode; /* Code-emit pointer; &regdummy = don't. */
@ -177,18 +177,18 @@ static long regsize; /* Code size. */
#ifndef STATIC
#define STATIC static
#endif
STATIC char *reg();
STATIC char *regbranch();
STATIC char *regpiece();
STATIC char *regatom();
STATIC char *regnode();
STATIC char *regnext();
STATIC void regc();
STATIC void reginsert();
STATIC void regtail();
STATIC void regoptail();
STATIC char *reg(int, int *);
STATIC char *regbranch(int *);
STATIC char *regpiece(int *);
STATIC char *regatom(int *);
STATIC char *regnode(char);
STATIC char *regnext(register char *);
STATIC void regc(char);
STATIC void reginsert(char, char *);
STATIC void regtail(char *, char *);
STATIC void regoptail(char *, char *);
#ifdef STRCSPN
STATIC int strcspn();
STATIC int strcspn(constant char *, constant char *);
#endif
/*
@ -207,8 +207,7 @@ STATIC int strcspn();
* of the structure of the compiled regexp.
*/
regexp *
regcomp(exp)
char *exp;
regcomp(constant char *exp)
{
register regexp *r;
register char *scan;
@ -297,9 +296,7 @@ char *exp;
* follows makes it hard to avoid.
*/
static char *
reg(paren, flagp)
int paren; /* Parenthesized? */
int *flagp;
reg(int paren, int *flagp)
{
register char *ret;
register char *br;
@ -369,8 +366,7 @@ int *flagp;
* Implements the concatenation operator.
*/
static char *
regbranch(flagp)
int *flagp;
regbranch(int *flagp)
{
register char *ret;
register char *chain;
@ -408,8 +404,7 @@ int *flagp;
* endmarker role is not redundant.
*/
static char *
regpiece(flagp)
int *flagp;
regpiece(int *flagp)
{
register char *ret;
register char op;
@ -472,8 +467,7 @@ int *flagp;
* separate node; the code is simpler that way and it's not worth fixing.
*/
static char *
regatom(flagp)
int *flagp;
regatom(int *flagp)
{
register char *ret;
int flags;
@ -583,8 +577,7 @@ int *flagp;
- regnode - emit a node
*/
static char * /* Location. */
regnode(op)
char op;
regnode(char op)
{
register char *ret;
register char *ptr;
@ -608,8 +601,7 @@ char op;
- regc - emit (if appropriate) a byte of code
*/
static void
regc(b)
char b;
regc(char b)
{
if (regcode != &regdummy)
*regcode++ = b;
@ -623,11 +615,9 @@ char b;
* Means relocating the operand.
*/
static void
reginsert(op, opnd)
char op;
char *opnd;
reginsert(char op, char *opnd)
{
register char *src;
register constant char *src;
register char *dst;
register char *place;
@ -652,9 +642,7 @@ char *opnd;
- regtail - set the next-pointer at the end of a node chain
*/
static void
regtail(p, val)
char *p;
char *val;
regtail(char *p, char *val)
{
register char *scan;
register char *temp;
@ -684,9 +672,7 @@ char *val;
- regoptail - regtail on operand of first argument; nop if operandless
*/
static void
regoptail(p, val)
char *p;
char *val;
regoptail(char *p, char *val)
{
/* "Operandless" and "op != BRANCH" are synonymous in practice. */
if (p == NULL || p == &regdummy || OP(p) != BRANCH)
@ -701,17 +687,17 @@ char *val;
/*
* Global work variables for regexec().
*/
static char *reginput; /* String-input pointer. */
static char *regbol; /* Beginning of input, for ^ check. */
static char **regstartp; /* Pointer to startp array. */
static char **regendp; /* Ditto for endp. */
static constant char *reginput; /* String-input pointer. */
static constant char *regbol; /* Beginning of input, for ^ check. */
static constant char **regstartp; /* Pointer to startp array. */
static constant char **regendp; /* Ditto for endp. */
/*
* Forwards.
*/
STATIC int regtry();
STATIC int regmatch();
STATIC int regrepeat();
STATIC int regtry(regexp *, constant char *);
STATIC int regmatch(char *);
STATIC int regrepeat(char *);
#ifdef DEBUG
int regnarrate = 0;
@ -723,12 +709,9 @@ STATIC char *regprop();
- regexec - match a regexp against a string
*/
int
regexec2(prog, string, notbol)
register regexp *prog;
register char *string;
int notbol;
regexec2(register regexp *prog, register constant char *string, int notbol)
{
register char *s;
register constant char *s;
/* Be paranoid... */
if (prog == NULL || string == NULL) {
@ -785,9 +768,7 @@ int notbol;
}
int
regexec(prog, string)
register regexp *prog;
register char *string;
regexec(register regexp *prog, register constant char *string)
{
return regexec2(prog, string, 0);
}
@ -796,13 +777,11 @@ register char *string;
- regtry - try match at specific point
*/
static int /* 0 failure, 1 success */
regtry(prog, string)
regexp *prog;
char *string;
regtry(regexp *prog, constant char *string)
{
register int i;
register char **sp;
register char **ep;
register constant char **sp;
register constant char **ep;
reginput = string;
regstartp = prog->startp;
@ -833,8 +812,7 @@ char *string;
* by recursion.
*/
static int /* 0 failure, 1 success */
regmatch(prog)
char *prog;
regmatch(char *prog)
{
register char *scan; /* Current node. */
char *next; /* Next node. */
@ -903,7 +881,7 @@ char *prog;
case OPEN+8:
case OPEN+9: {
register int no;
register char *save;
register constant char *save;
no = OP(scan) - OPEN;
save = reginput;
@ -932,7 +910,7 @@ char *prog;
case CLOSE+8:
case CLOSE+9: {
register int no;
register char *save;
register constant char *save;
no = OP(scan) - CLOSE;
save = reginput;
@ -952,7 +930,7 @@ char *prog;
/* NOTREACHED */
break;
case BRANCH: {
register char *save;
register constant char *save;
if (OP(next) != BRANCH) /* No choice. */
next = OPERAND(scan); /* Avoid recursion. */
@ -974,7 +952,7 @@ char *prog;
case PLUS: {
register char nextch;
register int no;
register char *save;
register constant char *save;
register int min;
/*
@ -1026,11 +1004,10 @@ char *prog;
- regrepeat - repeatedly match something simple, report how many
*/
static int
regrepeat(p)
char *p;
regrepeat(char *p)
{
register int count = 0;
register char *scan;
register constant char *scan;
register char *opnd;
scan = reginput;
@ -1072,8 +1049,7 @@ char *p;
- regnext - dig the "next" pointer out of a node
*/
static char *
regnext(p)
register char *p;
regnext(register char *p)
{
register int offset;
@ -1140,9 +1116,9 @@ regexp *r;
/*
- regprop - printable representation of opcode
*/
static char *
static constant char *
regprop(op)
char *op;
constant char *op;
{
register char *p;
static char buf[50];
@ -1233,9 +1209,7 @@ char *op;
*/
static int
strcspn(s1, s2)
char *s1;
char *s2;
strcspn(constant char *s1, constant char *s2)
{
register char *scan1;
register char *scan2;

View file

@ -10,8 +10,8 @@
#define NSUBEXP 10
typedef struct regexp {
char *startp[NSUBEXP];
char *endp[NSUBEXP];
constant char *startp[NSUBEXP];
constant char *endp[NSUBEXP];
char regstart; /* Internal use only. */
char reganch; /* Internal use only. */
char *regmust; /* Internal use only. */
@ -25,10 +25,10 @@ typedef struct regexp {
# define _ANSI_ARGS_(x) ()
#endif
extern regexp *regcomp _ANSI_ARGS_((char *exp));
extern int regexec _ANSI_ARGS_((regexp *prog, char *string));
extern int regexec2 _ANSI_ARGS_((regexp *prog, char *string, int notbol));
extern void regsub _ANSI_ARGS_((regexp *prog, char *source, char *dest));
extern void regerror _ANSI_ARGS_((char *msg));
extern regexp *regcomp _ANSI_ARGS_((constant char *exp));
extern int regexec _ANSI_ARGS_((regexp *prog, constant char *string));
extern int regexec2 _ANSI_ARGS_((regexp *prog, constant char *string, int notbol));
extern void regsub _ANSI_ARGS_((regexp *prog, constant char *source, char *dest));
extern void regerror _ANSI_ARGS_((constant char *msg));
#endif /* REGEXP */

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -28,13 +28,10 @@
public int sigs;
extern int sc_width, sc_height;
extern int screen_trashed;
extern int lnloop;
extern int linenums;
extern int wscroll;
extern int reading;
extern int quit_on_intr;
extern int secure;
extern long jump_sline_fraction;
extern int less_is_more;
@ -46,6 +43,7 @@ extern int less_is_more;
/* ARGSUSED*/
static RETSIGTYPE u_interrupt(int type)
{
(void) type;
bell();
#if OS2
LSIGNAL(SIGINT, SIG_ACK);
@ -78,6 +76,7 @@ static RETSIGTYPE u_interrupt(int type)
/* ARGSUSED*/
static RETSIGTYPE stop(int type)
{
(void) type;
LSIGNAL(SIGTSTP, stop);
sigs |= S_STOP;
if (reading)
@ -101,6 +100,7 @@ static RETSIGTYPE stop(int type)
/* ARGSUSED*/
public RETSIGTYPE winch(int type)
{
(void) type;
LSIGNAL(SIG_LESSWINDOW, winch);
sigs |= S_WINCH;
if (reading)
@ -135,6 +135,7 @@ static BOOL WINAPI wbreak_handler(DWORD dwCtrlType)
static RETSIGTYPE terminate(int type)
{
(void) type;
quit(15);
}
@ -154,7 +155,7 @@ public void init_signals(int on)
(void) LSIGNAL(SIGINT, u_interrupt);
#endif
#ifdef SIGTSTP
(void) LSIGNAL(SIGTSTP, secure ? SIG_IGN : stop);
(void) LSIGNAL(SIGTSTP, !secure_allow(SF_STOP) ? SIG_IGN : stop);
#endif
#ifdef SIGWINCH
(void) LSIGNAL(SIGWINCH, winch);
@ -235,7 +236,7 @@ public void psignals(void)
LSIGNAL(SIGTSTP, stop);
raw_mode(1);
init();
screen_trashed = 1;
screen_trashed();
tsignals |= S_WINCH;
}
#endif
@ -252,15 +253,18 @@ public void psignals(void)
if (sc_width != old_width || sc_height != old_height)
{
wscroll = (sc_height + 1) / 2;
calc_jump_sline();
calc_shift_count();
screen_size_changed();
}
screen_trashed = 1;
screen_trashed();
}
#endif
if (tsignals & S_INTERRUPT)
{
if (quit_on_intr)
quit(QUIT_INTERRUPT);
getcc_clear();
#if MSDOS_COMPILER==WIN32C
win32_getch_clear();
#endif
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -14,8 +14,8 @@
#if TAGS
public char ztags[] = "tags";
public char *tags = ztags;
public constant char ztags[] = "tags";
public constant char *tags = ztags;
static int total;
static int curseq;
@ -44,13 +44,13 @@ enum {
T_GPATH /* 'GPATH': path name (global) */
};
static enum tag_result findctag(char *tag);
static enum tag_result findgtag(char *tag, int type);
static char *nextgtag(void);
static char *prevgtag(void);
static enum tag_result findctag(constant char *tag);
static enum tag_result findgtag(constant char *tag, int type);
static constant char *nextgtag(void);
static constant char *prevgtag(void);
static POSITION ctagsearch(void);
static POSITION gtagsearch(void);
static int getentry(char *buf, char **tag, char **file, char **line);
static int getentry(char *buf, constant char **tag, constant char **file, constant char **line);
/*
* The list of tags generated by the last findgtag() call.
@ -69,7 +69,7 @@ struct tag {
char *tag_file; /* Source file containing the tag */
LINENUM tag_linenum; /* Appropriate line number in source file */
char *tag_pattern; /* Pattern used to find the tag */
char tag_endline; /* True if the pattern includes '$' */
lbool tag_endline; /* True if the pattern includes '$' */
};
#define TAG_END ((struct tag *) &taglist)
static struct taglist taglist = { TAG_END, TAG_END };
@ -111,7 +111,7 @@ public void cleantags(void)
/*
* Create a new tag entry.
*/
static struct tag * maketagent(char *name, char *file, LINENUM linenum, char *pattern, int endline)
static struct tag * maketagent(constant char *file, LINENUM linenum, constant char *pattern, lbool endline)
{
struct tag *tp;
@ -163,7 +163,7 @@ public int gettagtype(void)
* and "tagpattern" to the search pattern which should be used
* to find the tag.
*/
public void findtag(char *tag)
public void findtag(constant char *tag)
{
int type = gettagtype();
enum tag_result result;
@ -205,9 +205,9 @@ public POSITION tagsearch(void)
/*
* Go to the next tag.
*/
public char * nexttag(int n)
public constant char * nexttag(int n)
{
char *tagfile = (char *) NULL;
constant char *tagfile = (char *) NULL;
while (n-- > 0)
tagfile = nextgtag();
@ -217,9 +217,9 @@ public char * nexttag(int n)
/*
* Go to the previous tag.
*/
public char * prevtag(int n)
public constant char * prevtag(int n)
{
char *tagfile = (char *) NULL;
constant char *tagfile = (char *) NULL;
while (n-- > 0)
tagfile = prevgtag();
@ -250,18 +250,18 @@ public int curr_tag(void)
* Find tags in the "tags" file.
* Sets curtag to the first tag entry.
*/
static enum tag_result findctag(char *tag)
static enum tag_result findctag(constant char *tag)
{
char *p;
char *q;
FILE *f;
int taglen;
size_t taglen;
LINENUM taglinenum;
char *tagfile;
char *tagpattern;
int tagendline;
lbool tagendline;
int search_char;
int err;
lbool err;
char tline[TAGLINE_SIZE];
struct tag *tp;
@ -273,7 +273,7 @@ static enum tag_result findctag(char *tag)
cleantags();
total = 0;
taglen = (int) strlen(tag);
taglen = strlen(tag);
/*
* Search the tags file for the desired tag.
@ -320,7 +320,7 @@ static enum tag_result findctag(char *tag)
/*
* First see if it is a line number.
*/
tagendline = 0;
tagendline = FALSE;
taglinenum = getnum(&p, 0, &err);
if (err)
{
@ -353,7 +353,7 @@ static enum tag_result findctag(char *tag)
q--;
*q = '\0';
}
tp = maketagent(tag, tagfile, taglinenum, tagpattern, tagendline);
tp = maketagent(tagfile, taglinenum, tagpattern, tagendline);
TAG_INS(tp);
total++;
}
@ -384,7 +384,7 @@ static int curtag_match(char constant *line, POSITION linepos)
* If tagendline is set, make sure we match all
* the way to end of line (no extra chars after the match).
*/
int len = (int) strlen(curtag->tag_pattern);
size_t len = strlen(curtag->tag_pattern);
if (strncmp(curtag->tag_pattern, line, len) == 0 &&
(!curtag->tag_endline || line[len] == '\0' || line[len] == '\r'))
{
@ -407,8 +407,8 @@ static POSITION ctagsearch(void)
{
POSITION pos, linepos;
LINENUM linenum;
int line_len;
char *line;
size_t line_len;
constant char *line;
int found;
pos = ch_zero();
@ -456,7 +456,7 @@ static POSITION ctagsearch(void)
} else
{
int cvt_ops = CVT_ANSI;
int cvt_len = cvt_length(line_len, cvt_ops);
size_t cvt_len = cvt_length(line_len, cvt_ops);
int *chpos = cvt_alloc_chpos(cvt_len);
char *cline = (char *) ecalloc(1, cvt_len);
cvt_text(cline, line, chpos, &line_len, cvt_ops);
@ -481,7 +481,7 @@ static POSITION ctagsearch(void)
* for future use by gtagsearch().
* Sets curtag to the first tag entry.
*/
static enum tag_result findgtag(char *tag, int type)
static enum tag_result findgtag(constant char *tag, int type)
{
char buf[1024];
FILE *fp;
@ -510,7 +510,7 @@ static enum tag_result findgtag(char *tag, int type)
char *command;
char *flag;
char *qtag;
char *cmd = lgetenv("LESSGLOBALTAGS");
constant char *cmd = lgetenv("LESSGLOBALTAGS");
if (isnullenv(cmd))
return TAG_NOFILE;
@ -536,12 +536,11 @@ static enum tag_result findgtag(char *tag, int type)
/* Get our data from global(1). */
qtag = shell_quote(tag);
if (qtag == NULL)
qtag = tag;
qtag = save(tag);
command = (char *) ecalloc(strlen(cmd) + strlen(flag) +
strlen(qtag) + 5, sizeof(char));
sprintf(command, "%s -x%s %s", cmd, flag, qtag);
if (qtag != tag)
free(qtag);
free(qtag);
fp = popen(command, "r");
free(command);
#endif
@ -550,8 +549,10 @@ static enum tag_result findgtag(char *tag, int type)
{
while (fgets(buf, sizeof(buf), fp))
{
char *name, *file, *line;
int len;
constant char *name;
constant char *file;
constant char *line;
size_t len;
if (sigs)
{
@ -561,7 +562,7 @@ static enum tag_result findgtag(char *tag, int type)
#endif
return TAG_INTR;
}
len = (int) strlen(buf);
len = strlen(buf);
if (len > 0 && buf[len-1] == '\n')
buf[len-1] = '\0';
else
@ -582,7 +583,7 @@ static enum tag_result findgtag(char *tag, int type)
}
/* Make new entry and add to list. */
tp = maketagent(name, file, (LINENUM) atoi(line), NULL, 0);
tp = maketagent(file, (LINENUM) atoi(line), NULL, FALSE);
TAG_INS(tp);
total++;
}
@ -613,7 +614,7 @@ static int circular = 0; /* 1: circular tag structure */
* by findgtag(). The next call to gtagsearch() will try to position at the
* appropriate tag.
*/
static char * nextgtag(void)
static constant char * nextgtag(void)
{
struct tag *tp;
@ -642,7 +643,7 @@ static char * nextgtag(void)
* setup by findgtat(). The next call to gtagsearch() will try to position
* at the appropriate tag.
*/
static char * prevgtag(void)
static constant char * prevgtag(void)
{
struct tag *tp;
@ -688,7 +689,7 @@ static POSITION gtagsearch(void)
* |func 21 subr.c func(arg)
*
* The following commands write this format.
* o Traditinal Ctags with -x option
* o Traditional Ctags with -x option
* o Global with -x option
* See <http://www.gnu.org/software/global/global.html>
*
@ -706,7 +707,7 @@ static POSITION gtagsearch(void)
* The tag, file, and line will each be NUL-terminated pointers
* into buf.
*/
static int getentry(char *buf, char **tag, char **file, char **line)
static int getentry(char *buf, constant char **tag, constant char **file, constant char **line)
{
char *p = buf;

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -23,17 +23,29 @@
#define _WIN32_WINNT 0x400
#endif
#include <windows.h>
public DWORD console_mode;
#ifndef ENABLE_EXTENDED_FLAGS
#define ENABLE_EXTENDED_FLAGS 0x80
#define ENABLE_QUICK_EDIT_MODE 0x40
#endif
#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
#endif
public HANDLE tty;
public DWORD init_console_input_mode;
public DWORD curr_console_input_mode;
public DWORD base_console_input_mode;
public DWORD mouse_console_input_mode;
#else
public int tty;
#endif
extern int sigs;
#if LESSTEST
public char *ttyin_name = NULL;
public lbool is_lesstest(void)
{
return ttyin_name != NULL;
}
#endif /*LESSTEST*/
extern int sigs;
extern int utf_mode;
extern int wheel_lines;
#if !MSDOS_COMPILER
static int open_tty_device(constant char* dev)
@ -56,7 +68,7 @@ public int open_tty(void)
{
int fd = -1;
#if LESSTEST
if (ttyin_name != NULL)
if (is_lesstest())
fd = open_tty_device(ttyin_name);
#endif /*LESSTEST*/
#if HAVE_TTYNAME
@ -71,6 +83,10 @@ public int open_tty(void)
fd = open_tty_device("/dev/tty");
if (fd < 0)
fd = 2;
#ifdef __MVS__
struct f_cnvrt cvtreq = {SETCVTON, 0, 1047};
fcntl(fd, F_CONTROL_CVT, &cvtreq);
#endif
return fd;
}
#endif /* MSDOS_COMPILER */
@ -89,9 +105,14 @@ public void open_getchr(void)
tty = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, &sa,
OPEN_EXISTING, 0L, NULL);
GetConsoleMode(tty, &console_mode);
/* Make sure we get Ctrl+C events. */
SetConsoleMode(tty, ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT);
GetConsoleMode(tty, &init_console_input_mode);
/* base mode: ensure we get ctrl-C events, and don't get VT input. */
base_console_input_mode = (init_console_input_mode | ENABLE_PROCESSED_INPUT) & ~ENABLE_VIRTUAL_TERMINAL_INPUT;
/* mouse mode: enable mouse and disable quick edit. */
mouse_console_input_mode = (base_console_input_mode | ENABLE_MOUSE_INPUT | ENABLE_EXTENDED_FLAGS) & ~ENABLE_QUICK_EDIT_MODE;
/* Start with base mode. If --mouse is given, switch to mouse mode in init_mouse. */
curr_console_input_mode = base_console_input_mode;
SetConsoleMode(tty, curr_console_input_mode);
#else
#if MSDOS_COMPILER
extern int fd0;
@ -121,21 +142,21 @@ public void open_getchr(void)
public void close_getchr(void)
{
#if MSDOS_COMPILER==WIN32C
SetConsoleMode(tty, console_mode);
SetConsoleMode(tty, init_console_input_mode);
CloseHandle(tty);
#endif
}
#if MSDOS_COMPILER==WIN32C
/*
* Close the pipe, restoring the keyboard (CMD resets it, losing the mouse).
* Close the pipe, restoring the console mode (CMD resets it, losing the mouse).
*/
public int pclose(FILE *f)
{
int result;
result = _pclose(f);
SetConsoleMode(tty, ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT);
SetConsoleMode(tty, curr_console_input_mode);
return result;
}
#endif
@ -162,7 +183,7 @@ public int default_wheel_lines(void)
public int getchr(void)
{
char c;
int result;
ssize_t result;
do
{

View file

@ -1,4 +1,4 @@
/* Generated by "./mkutable -f2 Cc Cs Co Zl Zp -- unicode/UnicodeData.txt" on Mon Nov 14 18:19:24 PST 2022 */
/* Generated by "./mkutable -f2 Cc Cs Co Zl Zp -- unicode/UnicodeData.txt" on Sun Sep 17 17:56:27 PDT 2023 */
{ 0x0000, 0x0007 }, /* Cc */
{ 0x000b, 0x000b }, /* Cc */
{ 0x000e, 0x001f }, /* Cc */

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1984-2023 Mark Nudelman
* Copyright (C) 1984-2024 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.
@ -40,7 +40,7 @@ v19 5/2/85 Got rid of "verbose" eq_message().
v20 5/21/85 Fixed screen.c ioctls for System V.
v21 5/23/85 Fixed some first_cmd bugs.
v22 5/24/85 Added support for no RECOMP nor REGCMP.
v23 5/25/85 Miscellanous changes and prettying up.
v23 5/25/85 Miscellaneous changes and prettying up.
Posted to USENET.
-----------------------------------------------------------------
v24 6/3/85 Added ti,te terminal init & de-init.
@ -628,7 +628,7 @@ v373 1/14/02 Improve handling of filenames containing shell metachars.
v374 2/7/02 Fix memory leak; fix bug in -x argument parsing.
v375 4/7/02 Fix searching for SGR sequences; fix SECURE build;
add SGR support to DJGPP version (thanks to Eli Zaretskii).
v376 6/10/02 Fix bug in overstriking mulitbyte UTF-8 characters
v376 6/10/02 Fix bug in overstriking multibyte UTF-8 characters
(thanks to Jungshik Shin).
Posted to Web page.
-----------------------------------------------------------------
@ -993,6 +993,45 @@ v640 7/10/23 Add lesstest to release.
v641 7/10/23 Fix release.
v642 7/10/23 Fix release.
v643 7/20/23 Fix crash on Windows with -o.
v644 9/16/23 Improve ^C on non-terminated pipe; fix crash when files are
deleted; support large files and non-BMP chars on Windows;
fix # bug; don't filter header lines; fix shifting long lines;
add --match-shift.
v645 11/5/23 Default Windows charset is utf-8; update Unicode tables;
fix ESC-} bug; mouse right-click jumps to '#';
add LESSSECURE_ALLOW; add --lesskey-content & LESSKEY_CONTENT;
allow env var expansion in lesskey files; add LESS_UNSUPPORT.
v646 11/6/23 Bug fixes.
v647 11/16/23 Fix --+option; fix compiler warnings.
v648 11/16/23 Add lang.h to release.
v649 12/1/23 Add line number param to --header.
v650 2/6/24 Add --no-search-header-lines and --no-search-header-columns;
add ^L search modifier; add ^P shell command modifier;
add search wrap message; add ^O^N, ^O^P, ^O^L and ^O^O commands.
v651 3/4/24 Add --color attributes (*~_&); fix #/% substitution if name
contains spaces; allow --rscroll with non-ASCII char.
v652 3/11/24 Fix sleep_ms with usleep.
v653 3/20/24 Make default charset utf-8.
v654 4/28/24 Allow space to end parameter for some options; fix usleep bug;
fix bugs when filename contains control chars; fix DJGPP build.
v655 5/16/24 Fix search history bug with --incsearch.
v656 5/23/24 Fix bug using escape sequences in prompt.
v657 5/31/24 Fix buffer overrun when using LESSOPEN.
v658 6/13/24 Fix double-free in lesskey parser; fix crash using small value
for --line-num-width.
v659 6/20/24 Fix typo in help.
v660 6/24/24 Fix bug in ixerror.
v661 6/29/24 Simpler fix for ixerror bug.
v662 8/8/24 Fix build with --with-secure; improve true colors on Windows;
fix crash with --header; fix crash with -S; fix #stop;
fix --shift with fractional parameter; fix EOF bug in R command;
fix --header with short file; fix ^X bug when output is not tty.
v663 8/16/24 Fix ^X bug when output is not a tty.
v664 8/28/24 Fix Windows compile error, fix output bug on Windows with -Da.
v665 9/4/24 Fix ^Z bug.
v666 9/21/24 Fix missing first byte from LESSOPEN if >0x7f.
v667 9/26/24 Fix uninitialized variable in edit_ifile.
v668 10/6/24 Fix UTF-8 chars in prompt.
*/
char version[] = "643";
char version[] = "668";

View file

@ -1,4 +1,4 @@
/* Generated by "./mkutable -f1 W F -- unicode/EastAsianWidth.txt" on Mon Nov 14 18:19:24 PST 2022 */
/* Generated by "./mkutable -f1 W F -- unicode/EastAsianWidth.txt" on Sun Sep 17 17:56:27 PDT 2023 */
{ 0x1100, 0x115f }, /* W */
{ 0x231a, 0x231b }, /* W */
{ 0x2329, 0x232a }, /* W */
@ -37,7 +37,7 @@
{ 0x2e80, 0x2e99 }, /* W */
{ 0x2e9b, 0x2ef3 }, /* W */
{ 0x2f00, 0x2fd5 }, /* W */
{ 0x2ff0, 0x2ffb }, /* W */
{ 0x2ff0, 0x2fff }, /* W */
{ 0x3000, 0x3000 }, /* F */
{ 0x3001, 0x303e }, /* W */
{ 0x3041, 0x3096 }, /* W */
@ -45,7 +45,7 @@
{ 0x3105, 0x312f }, /* W */
{ 0x3131, 0x318e }, /* W */
{ 0x3190, 0x31e3 }, /* W */
{ 0x31f0, 0x321e }, /* W */
{ 0x31ef, 0x321e }, /* W */
{ 0x3220, 0x3247 }, /* W */
{ 0x3250, 0x4dbf }, /* W */
{ 0x4e00, 0xa48c }, /* W */

View file

@ -6,10 +6,19 @@
*/
public void xbuf_init(struct xbuffer *xbuf)
{
xbuf->data = NULL;
xbuf->size = xbuf->end = 0;
xbuf_init_size(xbuf, 16);
}
public void xbuf_init_size(struct xbuffer *xbuf, size_t init_size)
{
xbuf->data = NULL;
xbuf->size = xbuf->end = 0;
xbuf->init_size = init_size;
}
/*
* Free buffer space in an xbuf.
*/
public void xbuf_deinit(struct xbuffer *xbuf)
{
if (xbuf->data != NULL)
@ -17,20 +26,23 @@ public void xbuf_deinit(struct xbuffer *xbuf)
xbuf_init(xbuf);
}
/*
* Set xbuf to empty.
*/
public void xbuf_reset(struct xbuffer *xbuf)
{
xbuf->end = 0;
}
/*
* Add a byte to an expandable text buffer.
* Add a byte to an xbuf.
*/
public void xbuf_add_byte(struct xbuffer *xbuf, unsigned char b)
{
if (xbuf->end >= xbuf->size)
{
unsigned char *data;
if (ckd_add(&xbuf->size, xbuf->size, xbuf->size ? xbuf->size : 16))
if (ckd_add(&xbuf->size, xbuf->size, xbuf->size ? xbuf->size : xbuf->init_size))
out_of_memory();
data = (unsigned char *) ecalloc(xbuf->size, sizeof(unsigned char));
if (xbuf->data != NULL)
@ -40,16 +52,30 @@ public void xbuf_add_byte(struct xbuffer *xbuf, unsigned char b)
}
xbuf->data = data;
}
xbuf->data[xbuf->end++] = (unsigned char) b;
xbuf->data[xbuf->end++] = b;
}
public void xbuf_add_data(struct xbuffer *xbuf, unsigned char *data, int len)
/*
* Add a char to an xbuf.
*/
public void xbuf_add_char(struct xbuffer *xbuf, char c)
{
int i;
xbuf_add_byte(xbuf, (unsigned char) c);
}
/*
* Add arbitrary data to an xbuf.
*/
public void xbuf_add_data(struct xbuffer *xbuf, constant unsigned char *data, size_t len)
{
size_t i;
for (i = 0; i < len; i++)
xbuf_add_byte(xbuf, data[i]);
}
/*
* Remove the last byte from an xbuf.
*/
public int xbuf_pop(struct xbuffer *buf)
{
if (buf->end == 0)
@ -57,15 +83,21 @@ public int xbuf_pop(struct xbuffer *buf)
return (int) buf->data[--(buf->end)];
}
/*
* Set an xbuf to the contents of another xbuf.
*/
public void xbuf_set(struct xbuffer *dst, struct xbuffer *src)
{
xbuf_reset(dst);
xbuf_add_data(dst, src->data, src->end);
}
public char * xbuf_char_data(struct xbuffer *xbuf)
/*
* Return xbuf data as a char*.
*/
public constant char * xbuf_char_data(constant struct xbuffer *xbuf)
{
return (char *)(xbuf->data);
return (constant char *)(xbuf->data);
}
@ -84,7 +116,7 @@ public char * xbuf_char_data(struct xbuffer *xbuf)
* Otherwise, possibly set *R to an indeterminate value and return TRUE.
* R has size RSIZE, and is signed if and only if RSIGNED is nonzero.
*/
static int help_fixup(void *r, uintmax val, int rsize, int rsigned)
static lbool help_fixup(void *r, uintmax val, int rsize, int rsigned)
{
if (rsigned)
{
@ -100,14 +132,14 @@ static int help_fixup(void *r, uintmax val, int rsize, int rsigned)
long long *pr = r;
if (LLONG_MAX < val)
return TRUE;
*pr = val;
*pr = (long long) val;
#endif
#ifdef INTMAX_MAX
} else if (rsize == sizeof (intmax_t)) {
intmax_t *pr = r;
if (INTMAX_MAX < val)
return TRUE;
*pr = val;
*pr = (intmax_t) val;
#endif
} else /* rsize == sizeof (long) */
{
@ -129,32 +161,34 @@ static int help_fixup(void *r, uintmax val, int rsize, int rsigned)
*pr = (unsigned long) val;
#ifdef ULLONG_MAX
} else if (rsize == sizeof (unsigned long long)) {
long long *pr = r;
unsigned long long *pr = r;
if (ULLONG_MAX < val)
return TRUE;
*pr = val;
*pr = (unsigned long long) val;
#endif
} else /* rsize == sizeof (uintmax) */
{
uintmax *pr = r;
*pr = val;
*pr = (uintmax) val;
}
}
return FALSE;
}
/*
* If *R can represent the mathematical sum of A and B, store the sum
* and return FALSE. Otherwise, possibly set *R to an indeterminate
* value and return TRUE. R has size RSIZE, and is signed if and only
* if RSIGNED is nonzero.
*/
public int help_ckd_add(void *r, uintmax a, uintmax b, int rsize, int rsigned)
public lbool help_ckd_add(void *r, uintmax a, uintmax b, int rsize, int rsigned)
{
uintmax sum = a + b;
return sum < a || help_fixup(r, sum, rsize, rsigned);
}
/* Likewise, but for the product of A and B. */
public int help_ckd_mul(void *r, uintmax a, uintmax b, int rsize, int rsigned)
public lbool help_ckd_mul(void *r, uintmax a, uintmax b, int rsize, int rsigned)
{
uintmax product = a * b;
return ((b != 0 && a != product / b)

View file

@ -1,19 +1,24 @@
#ifndef XBUF_H_
#define XBUF_H_
#include "lang.h"
struct xbuffer
{
unsigned char *data;
int end;
int size;
size_t end;
size_t size;
size_t init_size;
};
void xbuf_init(struct xbuffer *xbuf);
void xbuf_init_size(struct xbuffer *xbuf, size_t init_size);
void xbuf_deinit(struct xbuffer *xbuf);
void xbuf_reset(struct xbuffer *xbuf);
void xbuf_add_byte(struct xbuffer *xbuf, unsigned char b);
void xbuf_add_data(struct xbuffer *xbuf, unsigned char *data, int len);
void xbuf_add_char(struct xbuffer *xbuf, char c);
void xbuf_add_data(struct xbuffer *xbuf, constant unsigned char *data, size_t len);
int xbuf_pop(struct xbuffer *xbuf);
char *xbuf_char_data(struct xbuffer *xbuf);
constant char *xbuf_char_data(constant struct xbuffer *xbuf);
#endif

View file

@ -1,7 +1,7 @@
PACKAGE= runtime
PROG= less
SRCS= main.c screen.c brac.c ch.c charset.c cmdbuf.c command.c cvt.c \
decode.c edit.c filename.c forwback.c help.c ifile.c input.c \
decode.c evar.c edit.c filename.c forwback.c help.c ifile.c input.c \
jump.c lesskey_parse.c \
line.c linenum.c lsystem.c mark.c optfunc.c option.c \
opttbl.c os.c output.c pattern.c position.c prompt.c search.c \

View file

@ -88,6 +88,12 @@
*/
#define LOGFILE (!SECURE)
/*
* OSC8_SEARCH is 1 if you wish to allow the ^O^O and related commands
* (to open OSC8 hyperlinks).
*/
#define OSC8_LINK 1
/*
* GNU_OPTIONS is 1 if you wish to support the GNU-style command
* line options --help and --version.
@ -138,7 +144,7 @@
/*
* HAVE_ANSI_PROTOS is 1 if your compiler supports ANSI function prototypes.
*/
#define HAVE_ANSI_PROTOS 1
#define HAVE_ANSI_PROTOS 1
/*
* HAVE_SYS_TYPES_H is 1 if your system has <sys/types.h>.
@ -235,7 +241,7 @@
/* Define to 1 if you have the <errno.h> header file. */
#define HAVE_ERRNO_H 1
/* Define to 1 if you have the `fchmod' function. */
/* Define to 1 if you have the 'fchmod' function. */
#define HAVE_FCHMOD 1
/* Define to 1 if you have the <fcntl.h> header file. */
@ -244,7 +250,7 @@
/* Define HAVE_FILENO if you have the fileno() macro. */
#define HAVE_FILENO 1
/* Define to 1 if you have the `fsync' function. */
/* Define to 1 if you have the 'fsync' function. */
#define HAVE_FSYNC 1
/* GNU regex library */
@ -262,7 +268,7 @@
/* Define HAVE_LOCALE if you have locale.h and setlocale. */
#define HAVE_LOCALE 1
/* Define to 1 if you have the `nanosleep' function. */
/* Define to 1 if you have the 'nanosleep' function. */
#define HAVE_NANOSLEEP 1
/* Define to 1 if you have the <ncursesw/termcap.h> header file. */
@ -280,20 +286,16 @@
/* PCRE2 (Perl-compatible regular expression) library */
/* #undef HAVE_PCRE2 */
/* Define to 1 if you have the `poll' function. */
/* Define to 1 if you have the 'poll' function. */
#define HAVE_POLL 1
/* Define to 1 if you have the `popen' function. */
/* Define to 1 if you have the 'popen' function. */
#define HAVE_POPEN 1
/* POSIX regcomp() and regex.h */
#define HAVE_POSIX_REGCOMP 1
/* Define HAVE_PROCFS if have have fstatfs with f_type and PROC_SUPER_MAGIC.
*/
/* #undef HAVE_PROCFS */
/* Define to 1 if you have the `realpath' function. */
/* Define to 1 if you have the 'realpath' function. */
#define HAVE_REALPATH 1
/* System V regcmp() */
@ -308,19 +310,19 @@
/* Define HAVE_SIGEMPTYSET if you have the sigemptyset macro. */
#define HAVE_SIGEMPTYSET 1
/* Define to 1 if you have the `sigprocmask' function. */
/* Define to 1 if you have the 'sigprocmask' function. */
#define HAVE_SIGPROCMASK 1
/* Define to 1 if you have the `sigsetmask' function. */
/* Define to 1 if you have the 'sigsetmask' function. */
#define HAVE_SIGSETMASK 1
/* Define to 1 if the system has the type `sigset_t'. */
/* Define to 1 if the system has the type 'sigset_t'. */
#define HAVE_SIGSET_T 1
/* Define to 1 if you have the `snprintf' function. */
/* Define to 1 if you have the 'snprintf' function. */
#define HAVE_SNPRINTF 1
/* Define to 1 if you have the `stat' function. */
/* Define to 1 if you have the 'stat' function. */
#define HAVE_STAT 1
/* Define HAVE_STAT_INO if your struct stat has st_ino and st_dev. */
@ -347,10 +349,10 @@
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the `strsignal' function. */
/* Define to 1 if you have the 'strsignal' function. */
#define HAVE_STRSIGNAL 1
/* Define to 1 if you have the `system' function. */
/* Define to 1 if you have the 'system' function. */
#define HAVE_SYSTEM 1
/* Define HAVE_SYS_ERRLIST if you have the sys_errlist[] variable. */
@ -389,7 +391,7 @@
/* Define HAVE_TIME_T if your system supports the "time_t" type. */
#define HAVE_TIME_T 1
/* Define to 1 if you have the `ttyname' function. */
/* Define to 1 if you have the 'ttyname' function. */
#define HAVE_TTYNAME 1
/* Define to 1 if you have the <unistd.h> header file. */
@ -398,7 +400,7 @@
/* Define HAVE_UPPER_LOWER if you have isupper, islower, toupper, tolower. */
#define HAVE_UPPER_LOWER 1
/* Define to 1 if you have the `usleep' function. */
/* Define to 1 if you have the 'usleep' function. */
#define HAVE_USLEEP 1
/* Henry Spencer V8 regcomp() and regexp.h */
@ -416,7 +418,7 @@
/* Define to 1 if you have the <wctype.h> header file. */
#define HAVE_WCTYPE_H 1
/* Define to 1 if you have the `_setjmp' function. */
/* Define to 1 if you have the '_setjmp' function. */
#define HAVE__SETJMP 1
/* Define MUST_DEFINE_ERRNO if you have errno but it is not define in errno.h.
@ -451,10 +453,10 @@
/* Define SECURE_COMPILE=1 to build a secure version of less. */
#define SECURE_COMPILE 0
/* Define to 1 if the `S_IS*' macros in <sys/stat.h> do not work properly. */
/* Define to 1 if the 'S_IS*' macros in <sys/stat.h> do not work properly. */
/* #undef STAT_MACROS_BROKEN */
/* Define to 1 if all of the C90 standard headers exist (not just the ones
/* Define to 1 if all of the C89 standard headers exist (not just the ones
required in a freestanding environment). This macro is provided for
backward compatibility; new code need not use it. */
#define STDC_HEADERS 1
@ -462,14 +464,20 @@
/* Number of bits in a file offset, on hosts where this is settable. */
/* #undef _FILE_OFFSET_BITS */
/* Define for large files, on AIX-style hosts. */
/* Define to 1 on platforms where this makes off_t a 64-bit type. */
/* #undef _LARGE_FILES */
/* Define to empty if `const' does not conform to ANSI C. */
/* Number of bits in time_t, on hosts where this is settable. */
/* #undef _TIME_BITS */
/* Define to 1 on platforms where this makes time_t a 64-bit type. */
/* #undef __MINGW_USE_VC2005_COMPAT */
/* Define to empty if 'const' does not conform to ANSI C. */
/* #undef const */
/* Define to `long int' if <sys/types.h> does not define. */
/* Define to 'long int' if <sys/types.h> does not define. */
/* #undef off_t */
/* Define to `unsigned int' if <sys/types.h> does not define. */
/* Define as 'unsigned int' if <stddef.h> doesn't define. */
/* #undef size_t */