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