xref: /freebsd/contrib/less/lmsg.c (revision 4523eebc6c1828d4fd41d7f1ab943cf079043bc8)
1*4523eebcSXin LI /*
2*4523eebcSXin LI  * Copyright (C) 1984-2026  Mark Nudelman
3*4523eebcSXin LI  *
4*4523eebcSXin LI  * You may distribute under the terms of either the GNU General Public
5*4523eebcSXin LI  * License or the Less License, as specified in the README file.
6*4523eebcSXin LI  *
7*4523eebcSXin LI  * For more information, see the README file.
8*4523eebcSXin LI  */
9*4523eebcSXin LI 
10*4523eebcSXin LI #include "less.h"
11*4523eebcSXin LI 
12*4523eebcSXin LI struct lmsg_sym { lessmsg_id id; constant char *sym; };
13*4523eebcSXin LI 
14*4523eebcSXin LI /*
15*4523eebcSXin LI  * lmsgs is the array of all messages, indexed by lmsg id.
16*4523eebcSXin LI  * Initialize it to the default messages.
17*4523eebcSXin LI  * Individual message can be overridden by a custom lessmsg file.
18*4523eebcSXin LI  */
19*4523eebcSXin LI #undef  M
20*4523eebcSXin LI #define M(sym,msg) msg,
21*4523eebcSXin LI static char *lmsgs[] = {
22*4523eebcSXin LI 	NULL, /* id 0 (LM_NULL) is not in lessmsg.inc */
23*4523eebcSXin LI #include "lessmsg.inc"
24*4523eebcSXin LI };
25*4523eebcSXin LI 
26*4523eebcSXin LI /*
27*4523eebcSXin LI  * lmsg_syms is the map of symbol names to lmsg ids.
28*4523eebcSXin LI  */
29*4523eebcSXin LI #undef  M
30*4523eebcSXin LI #define M(sym,msg) { LM_##sym, #sym },
31*4523eebcSXin LI static constant struct lmsg_sym lmsg_syms[] = {
32*4523eebcSXin LI #include "lessmsg.inc"
33*4523eebcSXin LI 	M(HELP,"")
34*4523eebcSXin LI };
35*4523eebcSXin LI 
36*4523eebcSXin LI /*
37*4523eebcSXin LI  * For historical reasons the help file is stored separately rather than
38*4523eebcSXin LI  * in the lmsgs array but it can still be overridden by a lessmsg file.
39*4523eebcSXin LI  */
40*4523eebcSXin LI public char *help_data;
41*4523eebcSXin LI public int size_help_data;
42*4523eebcSXin LI extern char helpdata[];
43*4523eebcSXin LI extern int size_helpdata;
44*4523eebcSXin LI 
45*4523eebcSXin LI /*
46*4523eebcSXin LI  * Report an error in a lessmsg file.
47*4523eebcSXin LI  */
48*4523eebcSXin LI static void lmsg_error(constant char *err, constant char *lmsg_file, int linenum)
49*4523eebcSXin LI {
50*4523eebcSXin LI 	PARG parg[3];
51*4523eebcSXin LI 	parg[0].p_string = lmsg_file;
52*4523eebcSXin LI 	parg[1].p_int = linenum;
53*4523eebcSXin LI 	parg[2].p_string = err;
54*4523eebcSXin LI 	error("Error in lessmsg file %s line %d: %s", parg);
55*4523eebcSXin LI }
56*4523eebcSXin LI 
57*4523eebcSXin LI /*
58*4523eebcSXin LI  * Return a numeric lmsg id given a symbolic name.
59*4523eebcSXin LI  */
60*4523eebcSXin LI static lessmsg_id lmsg_id(constant char *sym)
61*4523eebcSXin LI {
62*4523eebcSXin LI 	size_t i;
63*4523eebcSXin LI 	for (i = 0; i < countof(lmsg_syms); i++)
64*4523eebcSXin LI 		if (strcmp(sym, lmsg_syms[i].sym) == 0)
65*4523eebcSXin LI 			return lmsg_syms[i].id;
66*4523eebcSXin LI 	return LM_NULL;
67*4523eebcSXin LI }
68*4523eebcSXin LI 
69*4523eebcSXin LI /*
70*4523eebcSXin LI  * Return a numeric char value from a string.
71*4523eebcSXin LI  */
72*4523eebcSXin LI static char numeric_char(char **pp, int radix, constant char *lmsg_file, int linenum)
73*4523eebcSXin LI {
74*4523eebcSXin LI 	constant int max_chars = (radix == 16) ? 2 : 3;
75*4523eebcSXin LI 	char nbuf[4]; /* max_chars+1 */
76*4523eebcSXin LI 	char *p = *pp;
77*4523eebcSXin LI 	int val;
78*4523eebcSXin LI 	strncpy(nbuf, p, max_chars);
79*4523eebcSXin LI 	nbuf[max_chars] = '\0';
80*4523eebcSXin LI 	val = lstrtoi(nbuf, &p, radix);
81*4523eebcSXin LI 	if (p == nbuf || val < 0 || val > 255)
82*4523eebcSXin LI 	{
83*4523eebcSXin LI 		lmsg_error("invalid numeric char value", lmsg_file, linenum);
84*4523eebcSXin LI 		return '?';
85*4523eebcSXin LI 	}
86*4523eebcSXin LI 	*pp += ptr_diff(p,nbuf);
87*4523eebcSXin LI 	return (char) val;
88*4523eebcSXin LI }
89*4523eebcSXin LI 
90*4523eebcSXin LI /*
91*4523eebcSXin LI  * Process one character in a lessmsg file.
92*4523eebcSXin LI  */
93*4523eebcSXin LI static char * lmsg_char(char *mp, char *line, size_t line_size, FILE* fd, struct xbuffer *xbuf, constant char *lmsg_file, int *linenum)
94*4523eebcSXin LI {
95*4523eebcSXin LI 	if (*mp != '\\')
96*4523eebcSXin LI 		xbuf_add_char(xbuf, *mp++);
97*4523eebcSXin LI 	else
98*4523eebcSXin LI 	{
99*4523eebcSXin LI 		/* Handle backslash escape. */
100*4523eebcSXin LI 		switch (*++mp)
101*4523eebcSXin LI 		{
102*4523eebcSXin LI 		case '\0': break;
103*4523eebcSXin LI 		case 'n': xbuf_add_char(xbuf, '\n'); ++mp; break;
104*4523eebcSXin LI 		case 'r': xbuf_add_char(xbuf, '\r'); ++mp; break;
105*4523eebcSXin LI 		case 't': xbuf_add_char(xbuf, '\t'); ++mp; break;
106*4523eebcSXin LI 		case '\n': case '\r': /* continued on next line */
107*4523eebcSXin LI 			if (fgets(line, line_size, fd) != NULL)
108*4523eebcSXin LI 			{
109*4523eebcSXin LI 				++*linenum;
110*4523eebcSXin LI 				mp = line;
111*4523eebcSXin LI 			}
112*4523eebcSXin LI 			break;
113*4523eebcSXin LI 		case 'x': /* hex value */
114*4523eebcSXin LI 			++mp;
115*4523eebcSXin LI 			xbuf_add_char(xbuf, numeric_char(&mp, 16, lmsg_file, *linenum));
116*4523eebcSXin LI 			break;
117*4523eebcSXin LI 		default:
118*4523eebcSXin LI 			if (*mp >= '0' && *mp <= '7') /* octal value */
119*4523eebcSXin LI 				xbuf_add_char(xbuf, numeric_char(&mp, 8, lmsg_file, *linenum));
120*4523eebcSXin LI 			else /* literal char */
121*4523eebcSXin LI 				xbuf_add_char(xbuf, *mp++);
122*4523eebcSXin LI 			break;
123*4523eebcSXin LI 		}
124*4523eebcSXin LI 	}
125*4523eebcSXin LI 	return mp;
126*4523eebcSXin LI }
127*4523eebcSXin LI 
128*4523eebcSXin LI /*
129*4523eebcSXin LI  * Process one line in a lessmsg file.
130*4523eebcSXin LI  */
131*4523eebcSXin LI static lbool lmsg_line(char *line, size_t line_size, FILE* fd, struct xbuffer *xbuf, constant char *lmsg_file, int *linenum)
132*4523eebcSXin LI {
133*4523eebcSXin LI 	lessmsg_id id;
134*4523eebcSXin LI 	char *msg;
135*4523eebcSXin LI 	char *mp;
136*4523eebcSXin LI 	char *sym = skipsp(line);
137*4523eebcSXin LI 
138*4523eebcSXin LI 	if (sym[0] == '\n' || sym[0] == '\r' || sym[0] == '#')
139*4523eebcSXin LI 		return TRUE; /* skip comments and blank lines */
140*4523eebcSXin LI 	msg = strchr(sym, ' ');
141*4523eebcSXin LI 	if (msg == NULL)
142*4523eebcSXin LI 	{
143*4523eebcSXin LI 		lmsg_error("missing space", lmsg_file, *linenum);
144*4523eebcSXin LI 		return FALSE;
145*4523eebcSXin LI 	}
146*4523eebcSXin LI 	*msg++ = '\0'; /* terminate symbol */
147*4523eebcSXin LI 	msg = skipsp(msg);
148*4523eebcSXin LI 	id = lmsg_id(sym);
149*4523eebcSXin LI 	if (id == 0)
150*4523eebcSXin LI 	{
151*4523eebcSXin LI 		lmsg_error("unknown lessmsg symbol", lmsg_file, *linenum);
152*4523eebcSXin LI 		return FALSE;
153*4523eebcSXin LI 	}
154*4523eebcSXin LI 	/* Copy the message string into xbuf. */
155*4523eebcSXin LI 	xbuf_reset(xbuf);
156*4523eebcSXin LI 	for (mp = msg;  *mp != '\0' && *mp != '\n' && *mp != '\r'; )
157*4523eebcSXin LI 		mp = lmsg_char(mp, line, line_size, fd, xbuf, lmsg_file, linenum);
158*4523eebcSXin LI 	/* Store the message string in the table. */
159*4523eebcSXin LI 	msg = saven(xbuf_char_data(xbuf), xbuf->end);
160*4523eebcSXin LI 	if (id == LM_HELP)
161*4523eebcSXin LI 	{
162*4523eebcSXin LI 		help_data = msg;
163*4523eebcSXin LI 		size_help_data = xbuf->end;
164*4523eebcSXin LI 	} else
165*4523eebcSXin LI 		lmsgs[id] = msg;
166*4523eebcSXin LI 	return TRUE;
167*4523eebcSXin LI }
168*4523eebcSXin LI 
169*4523eebcSXin LI /*
170*4523eebcSXin LI  * Initialize the lmsgs array from an lmsg file.
171*4523eebcSXin LI  */
172*4523eebcSXin LI public void lmsg_init(constant char *lmsg_file)
173*4523eebcSXin LI {
174*4523eebcSXin LI 	FILE *fd;
175*4523eebcSXin LI 	char line[512];
176*4523eebcSXin LI 	struct xbuffer xbuf;
177*4523eebcSXin LI 	int linenum;
178*4523eebcSXin LI 	PARG parg;
179*4523eebcSXin LI 
180*4523eebcSXin LI 	help_data = helpdata;
181*4523eebcSXin LI 	size_help_data = size_helpdata;
182*4523eebcSXin LI 	if (isnullenv(lmsg_file))
183*4523eebcSXin LI 		return;
184*4523eebcSXin LI 
185*4523eebcSXin LI 	fd = fopen(lmsg_file, "r");
186*4523eebcSXin LI 	if (fd == NULL)
187*4523eebcSXin LI 	{
188*4523eebcSXin LI 		parg.p_string = lmsg_file;
189*4523eebcSXin LI 		error("Cannot open lessmsg file %s", &parg);
190*4523eebcSXin LI 		return;
191*4523eebcSXin LI 	}
192*4523eebcSXin LI 	xbuf_init(&xbuf); /* for efficiency, init once here and reuse for each line */
193*4523eebcSXin LI 	linenum = 0;
194*4523eebcSXin LI 	while (fgets(line, sizeof(line), fd) != NULL)
195*4523eebcSXin LI 	{
196*4523eebcSXin LI 		++linenum;
197*4523eebcSXin LI 		lmsg_line(line, sizeof(line), fd, &xbuf, lmsg_file, &linenum);
198*4523eebcSXin LI 	}
199*4523eebcSXin LI 	xbuf_deinit(&xbuf);
200*4523eebcSXin LI 	fclose(fd);
201*4523eebcSXin LI }
202*4523eebcSXin LI 
203*4523eebcSXin LI /*
204*4523eebcSXin LI  * Return the string associated with a numeric lmsg id.
205*4523eebcSXin LI  */
206*4523eebcSXin LI public constant char * lmsg(lessmsg_id id)
207*4523eebcSXin LI {
208*4523eebcSXin LI #if 1
209*4523eebcSXin LI 	if (id <= 0 || id >= countof(lmsgs))
210*4523eebcSXin LI 	{
211*4523eebcSXin LI 		PARG parg;
212*4523eebcSXin LI 		parg.p_int = (int) id;
213*4523eebcSXin LI 		error("invalid lessmsg id %d", &parg);
214*4523eebcSXin LI 		return NULL;
215*4523eebcSXin LI 	}
216*4523eebcSXin LI #endif
217*4523eebcSXin LI 	return lmsgs[id];
218*4523eebcSXin LI }
219