1a8f92a7cSPaul Saab /* $FreeBSD$ */ 2a5f0fb15SPaul Saab /* 3*c77c4889SXin LI * Copyright (C) 1984-2024 Mark Nudelman 4a5f0fb15SPaul Saab * 5a5f0fb15SPaul Saab * You may distribute under the terms of either the GNU General Public 6a5f0fb15SPaul Saab * License or the Less License, as specified in the README file. 7a5f0fb15SPaul Saab * 896e55cc7SXin LI * For more information, see the README file. 9a5f0fb15SPaul Saab */ 10a5f0fb15SPaul Saab 11a5f0fb15SPaul Saab 12a5f0fb15SPaul Saab /* 13a5f0fb15SPaul Saab * Entry point, initialization, miscellaneous routines. 14a5f0fb15SPaul Saab */ 15a5f0fb15SPaul Saab 16a5f0fb15SPaul Saab #include "less.h" 178fd4165cSPaul Saab #if MSDOS_COMPILER==WIN32C 18b7780dbeSXin LI #define WIN32_LEAN_AND_MEAN 198fd4165cSPaul Saab #include <windows.h> 20*c77c4889SXin LI 21*c77c4889SXin LI #if defined(MINGW) || defined(_MSC_VER) 22*c77c4889SXin LI #include <locale.h> 23*c77c4889SXin LI #include <shellapi.h> 248fd4165cSPaul Saab #endif 25a5f0fb15SPaul Saab 26*c77c4889SXin LI public unsigned less_acp = CP_ACP; 27*c77c4889SXin LI #endif 28*c77c4889SXin LI 29*c77c4889SXin LI #include "option.h" 30*c77c4889SXin LI 31a5f0fb15SPaul Saab public char * every_first_cmd = NULL; 32*c77c4889SXin LI public lbool new_file; 33a5f0fb15SPaul Saab public int is_tty; 34a5f0fb15SPaul Saab public IFILE curr_ifile = NULL_IFILE; 35a5f0fb15SPaul Saab public IFILE old_ifile = NULL_IFILE; 36a5f0fb15SPaul Saab public struct scrpos initial_scrpos; 37a5f0fb15SPaul Saab public POSITION start_attnpos = NULL_POSITION; 38a5f0fb15SPaul Saab public POSITION end_attnpos = NULL_POSITION; 39a5f0fb15SPaul Saab public int wscroll; 40*c77c4889SXin LI public constant char *progname; 41a5f0fb15SPaul Saab public int quitting; 42a5f0fb15SPaul Saab public int dohelp; 43*c77c4889SXin LI public char * init_header = NULL; 44*c77c4889SXin LI static int secure_allow_features; 45a5f0fb15SPaul Saab 46a5f0fb15SPaul Saab #if LOGFILE 47a5f0fb15SPaul Saab public int logfile = -1; 48*c77c4889SXin LI public lbool force_logfile = FALSE; 49a5f0fb15SPaul Saab public char * namelogfile = NULL; 50a5f0fb15SPaul Saab #endif 51a5f0fb15SPaul Saab 52a5f0fb15SPaul Saab #if EDITOR 53*c77c4889SXin LI public constant char * editor; 54*c77c4889SXin LI public constant char * editproto; 55a5f0fb15SPaul Saab #endif 56a5f0fb15SPaul Saab 57a5f0fb15SPaul Saab #if TAGS 588fd4165cSPaul Saab extern char * tags; 59a5f0fb15SPaul Saab extern char * tagoption; 60a5f0fb15SPaul Saab extern int jump_sline; 61a5f0fb15SPaul Saab #endif 62a5f0fb15SPaul Saab 638fd4165cSPaul Saab #ifdef WIN32 64*c77c4889SXin LI static wchar_t consoleTitle[256]; 658fd4165cSPaul Saab #endif 668fd4165cSPaul Saab 67b7780dbeSXin LI public int one_screen; 6833096f16SXin LI extern int less_is_more; 69a5f0fb15SPaul Saab extern int missing_cap; 70a5f0fb15SPaul Saab extern int know_dumb; 71f6b74a7dSXin LI extern int quit_if_one_screen; 72b7780dbeSXin LI extern int no_init; 732235c7feSXin LI extern int errmsgs; 7495270f73SXin LI extern int redraw_on_quit; 7595270f73SXin LI extern int term_init_done; 7695270f73SXin LI extern int first_time; 77a5f0fb15SPaul Saab 78*c77c4889SXin LI #if MSDOS_COMPILER==WIN32C && (defined(MINGW) || defined(_MSC_VER)) 79*c77c4889SXin LI /* malloc'ed 0-terminated utf8 of 0-terminated wide ws, or null on errors */ 80*c77c4889SXin LI static char *utf8_from_wide(constant wchar_t *ws) 81*c77c4889SXin LI { 82*c77c4889SXin LI char *u8 = NULL; 83*c77c4889SXin LI int n = WideCharToMultiByte(CP_UTF8, 0, ws, -1, NULL, 0, NULL, NULL); 84*c77c4889SXin LI if (n > 0) 85*c77c4889SXin LI { 86*c77c4889SXin LI u8 = ecalloc(n, sizeof(char)); 87*c77c4889SXin LI WideCharToMultiByte(CP_UTF8, 0, ws, -1, u8, n, NULL, NULL); 88*c77c4889SXin LI } 89*c77c4889SXin LI return u8; 90*c77c4889SXin LI } 91*c77c4889SXin LI 92*c77c4889SXin LI /* 93*c77c4889SXin LI * similar to using UTF8 manifest to make the ANSI APIs UTF8, but dynamically 94*c77c4889SXin LI * with setlocale. unlike the manifest, argv and environ are already ACP, so 95*c77c4889SXin LI * make them UTF8. Additionally, this affects only the libc/crt API, and so 96*c77c4889SXin LI * e.g. fopen filename becomes UTF-8, but CreateFileA filename remains CP_ACP. 97*c77c4889SXin LI * CP_ACP remains the original codepage - use the dynamic less_acp instead. 98*c77c4889SXin LI * effective on win 10 1803 or later when compiled with ucrt, else no-op. 99*c77c4889SXin LI */ 100*c77c4889SXin LI static void try_utf8_locale(int *pargc, constant char ***pargv) 101*c77c4889SXin LI { 102*c77c4889SXin LI char *locale_orig = strdup(setlocale(LC_ALL, NULL)); 103*c77c4889SXin LI wchar_t **wargv = NULL, *wenv, *wp; 104*c77c4889SXin LI constant char **u8argv; 105*c77c4889SXin LI char *u8e; 106*c77c4889SXin LI int i, n; 107*c77c4889SXin LI 108*c77c4889SXin LI if (!setlocale(LC_ALL, ".UTF8")) 109*c77c4889SXin LI goto cleanup; /* not win10 1803+ or not ucrt */ 110*c77c4889SXin LI 111*c77c4889SXin LI /* 112*c77c4889SXin LI * wargv is before glob expansion. some ucrt builds may expand globs 113*c77c4889SXin LI * before main is entered, so n may be smaller than the original argc. 114*c77c4889SXin LI * that's ok, because later code at main expands globs anyway. 115*c77c4889SXin LI */ 116*c77c4889SXin LI wargv = CommandLineToArgvW(GetCommandLineW(), &n); 117*c77c4889SXin LI if (!wargv) 118*c77c4889SXin LI goto bad_args; 119*c77c4889SXin LI 120*c77c4889SXin LI u8argv = (constant char **) ecalloc(n + 1, sizeof(char *)); 121*c77c4889SXin LI for (i = 0; i < n; ++i) 122*c77c4889SXin LI { 123*c77c4889SXin LI if (!(u8argv[i] = utf8_from_wide(wargv[i]))) 124*c77c4889SXin LI goto bad_args; 125*c77c4889SXin LI } 126*c77c4889SXin LI u8argv[n] = 0; 127*c77c4889SXin LI 128*c77c4889SXin LI less_acp = CP_UTF8; 129*c77c4889SXin LI *pargc = n; 130*c77c4889SXin LI *pargv = u8argv; /* leaked on exit */ 131*c77c4889SXin LI 132*c77c4889SXin LI /* convert wide env to utf8 where we can, but don't abort on errors */ 133*c77c4889SXin LI if ((wenv = GetEnvironmentStringsW())) 134*c77c4889SXin LI { 135*c77c4889SXin LI for (wp = wenv; *wp; wp += wcslen(wp) + 1) 136*c77c4889SXin LI { 137*c77c4889SXin LI if ((u8e = utf8_from_wide(wp))) 138*c77c4889SXin LI _putenv(u8e); 139*c77c4889SXin LI free(u8e); /* windows putenv makes a copy */ 140*c77c4889SXin LI } 141*c77c4889SXin LI FreeEnvironmentStringsW(wenv); 142*c77c4889SXin LI } 143*c77c4889SXin LI 144*c77c4889SXin LI goto cleanup; 145*c77c4889SXin LI 146*c77c4889SXin LI bad_args: 147*c77c4889SXin LI error("WARNING: cannot use unicode arguments", NULL_PARG); 148*c77c4889SXin LI setlocale(LC_ALL, locale_orig); 149*c77c4889SXin LI 150*c77c4889SXin LI cleanup: 151*c77c4889SXin LI free(locale_orig); 152*c77c4889SXin LI LocalFree(wargv); 153*c77c4889SXin LI } 154*c77c4889SXin LI #endif 155*c77c4889SXin LI 156*c77c4889SXin LI static int security_feature_error(constant char *type, size_t len, constant char *name) 157*c77c4889SXin LI { 158*c77c4889SXin LI PARG parg; 159*c77c4889SXin LI size_t msglen = len + strlen(type) + 64; 160*c77c4889SXin LI char *msg = ecalloc(msglen, sizeof(char)); 161*c77c4889SXin LI SNPRINTF3(msg, msglen, "LESSSECURE_ALLOW: %s feature name \"%.*s\"", type, (int) len, name); 162*c77c4889SXin LI parg.p_string = msg; 163*c77c4889SXin LI error("%s", &parg); 164*c77c4889SXin LI free(msg); 165*c77c4889SXin LI return 0; 166*c77c4889SXin LI } 167*c77c4889SXin LI 168*c77c4889SXin LI /* 169*c77c4889SXin LI * Return the SF_xxx value of a secure feature given the name of the feature. 170*c77c4889SXin LI */ 171*c77c4889SXin LI static int security_feature(constant char *name, size_t len) 172*c77c4889SXin LI { 173*c77c4889SXin LI struct secure_feature { constant char *name; int sf_value; }; 174*c77c4889SXin LI static struct secure_feature features[] = { 175*c77c4889SXin LI { "edit", SF_EDIT }, 176*c77c4889SXin LI { "examine", SF_EXAMINE }, 177*c77c4889SXin LI { "glob", SF_GLOB }, 178*c77c4889SXin LI { "history", SF_HISTORY }, 179*c77c4889SXin LI { "lesskey", SF_LESSKEY }, 180*c77c4889SXin LI { "lessopen", SF_LESSOPEN }, 181*c77c4889SXin LI { "logfile", SF_LOGFILE }, 182*c77c4889SXin LI { "osc8", SF_OSC8_OPEN }, 183*c77c4889SXin LI { "pipe", SF_PIPE }, 184*c77c4889SXin LI { "shell", SF_SHELL }, 185*c77c4889SXin LI { "stop", SF_STOP }, 186*c77c4889SXin LI { "tags", SF_TAGS }, 187*c77c4889SXin LI }; 188*c77c4889SXin LI int i; 189*c77c4889SXin LI int match = -1; 190*c77c4889SXin LI 191*c77c4889SXin LI for (i = 0; i < countof(features); i++) 192*c77c4889SXin LI { 193*c77c4889SXin LI if (strncmp(features[i].name, name, len) == 0) 194*c77c4889SXin LI { 195*c77c4889SXin LI if (match >= 0) /* name is ambiguous */ 196*c77c4889SXin LI return security_feature_error("ambiguous", len, name); 197*c77c4889SXin LI match = i; 198*c77c4889SXin LI } 199*c77c4889SXin LI } 200*c77c4889SXin LI if (match < 0) 201*c77c4889SXin LI return security_feature_error("invalid", len, name); 202*c77c4889SXin LI return features[match].sf_value; 203*c77c4889SXin LI } 204*c77c4889SXin LI 205*c77c4889SXin LI /* 206*c77c4889SXin LI * Set the secure_allow_features bitmask, which controls 207*c77c4889SXin LI * whether certain secure features are allowed. 208*c77c4889SXin LI */ 209*c77c4889SXin LI static void init_secure(void) 210*c77c4889SXin LI { 211*c77c4889SXin LI #if SECURE 212*c77c4889SXin LI secure_allow_features = 0; 213*c77c4889SXin LI #else 214*c77c4889SXin LI constant char *str = lgetenv("LESSSECURE"); 215*c77c4889SXin LI if (isnullenv(str)) 216*c77c4889SXin LI secure_allow_features = ~0; /* allow everything */ 217*c77c4889SXin LI else 218*c77c4889SXin LI secure_allow_features = 0; /* allow nothing */ 219*c77c4889SXin LI 220*c77c4889SXin LI str = lgetenv("LESSSECURE_ALLOW"); 221*c77c4889SXin LI if (!isnullenv(str)) 222*c77c4889SXin LI { 223*c77c4889SXin LI for (;;) 224*c77c4889SXin LI { 225*c77c4889SXin LI constant char *estr; 226*c77c4889SXin LI while (*str == ' ' || *str == ',') ++str; /* skip leading spaces/commas */ 227*c77c4889SXin LI if (*str == '\0') break; 228*c77c4889SXin LI estr = strchr(str, ','); 229*c77c4889SXin LI if (estr == NULL) estr = str + strlen(str); 230*c77c4889SXin LI while (estr > str && estr[-1] == ' ') --estr; /* trim trailing spaces */ 231*c77c4889SXin LI secure_allow_features |= security_feature(str, ptr_diff(estr, str)); 232*c77c4889SXin LI str = estr; 233*c77c4889SXin LI } 234*c77c4889SXin LI } 235*c77c4889SXin LI #endif 236*c77c4889SXin LI } 237*c77c4889SXin LI 238a5f0fb15SPaul Saab /* 239a5f0fb15SPaul Saab * Entry point. 240a5f0fb15SPaul Saab */ 241*c77c4889SXin LI int main(int argc, constant char *argv[]) 242a5f0fb15SPaul Saab { 243a5f0fb15SPaul Saab IFILE ifile; 244*c77c4889SXin LI constant char *s; 245*c77c4889SXin LI 246*c77c4889SXin LI #if MSDOS_COMPILER==WIN32C && (defined(MINGW) || defined(_MSC_VER)) 247*c77c4889SXin LI if (GetACP() != CP_UTF8) /* not using a UTF-8 manifest */ 248*c77c4889SXin LI try_utf8_locale(&argc, &argv); 249*c77c4889SXin LI #endif 250a5f0fb15SPaul Saab 251a5f0fb15SPaul Saab #ifdef __EMX__ 252a5f0fb15SPaul Saab _response(&argc, &argv); 253a5f0fb15SPaul Saab _wildcard(&argc, &argv); 254a5f0fb15SPaul Saab #endif 255a5f0fb15SPaul Saab 256a5f0fb15SPaul Saab progname = *argv++; 257a5f0fb15SPaul Saab argc--; 258*c77c4889SXin LI init_secure(); 259a5f0fb15SPaul Saab 260a5f0fb15SPaul Saab #ifdef WIN32 261a5f0fb15SPaul Saab if (getenv("HOME") == NULL) 262a5f0fb15SPaul Saab { 263a5f0fb15SPaul Saab /* 264a5f0fb15SPaul Saab * If there is no HOME environment variable, 265a5f0fb15SPaul Saab * try the concatenation of HOMEDRIVE + HOMEPATH. 266a5f0fb15SPaul Saab */ 267a5f0fb15SPaul Saab char *drive = getenv("HOMEDRIVE"); 268a5f0fb15SPaul Saab char *path = getenv("HOMEPATH"); 269a5f0fb15SPaul Saab if (drive != NULL && path != NULL) 270a5f0fb15SPaul Saab { 271a5f0fb15SPaul Saab char *env = (char *) ecalloc(strlen(drive) + 272a5f0fb15SPaul Saab strlen(path) + 6, sizeof(char)); 273a5f0fb15SPaul Saab strcpy(env, "HOME="); 274a5f0fb15SPaul Saab strcat(env, drive); 275a5f0fb15SPaul Saab strcat(env, path); 276a5f0fb15SPaul Saab putenv(env); 277a5f0fb15SPaul Saab } 278a5f0fb15SPaul Saab } 279*c77c4889SXin LI /* on failure, consoleTitle is already a valid empty string */ 280*c77c4889SXin LI GetConsoleTitleW(consoleTitle, countof(consoleTitle)); 281a5f0fb15SPaul Saab #endif /* WIN32 */ 282a5f0fb15SPaul Saab 283a5f0fb15SPaul Saab /* 284a5f0fb15SPaul Saab * Process command line arguments and LESS environment arguments. 285a5f0fb15SPaul Saab * Command line arguments override environment arguments. 286a5f0fb15SPaul Saab */ 287a5f0fb15SPaul Saab is_tty = isatty(1); 288b7780dbeSXin LI init_mark(); 289a5f0fb15SPaul Saab init_cmds(); 290d713e089SXin LI init_poll(); 291a5f0fb15SPaul Saab init_charset(); 292a5f0fb15SPaul Saab init_line(); 29389dd99dcSXin LI init_cmdhist(); 294a5f0fb15SPaul Saab init_option(); 295f0be0a1fSXin LI init_search(); 296a8f92a7cSPaul Saab 297720c436cSXin LI /* 298720c436cSXin LI * If the name of the executable program is "more", 299720c436cSXin LI * act like LESS_IS_MORE is set. 300720c436cSXin LI */ 301*c77c4889SXin LI if (strcmp(last_component(progname), "more") == 0) { 302720c436cSXin LI less_is_more = 1; 303*c77c4889SXin LI scan_option("-fG"); 304*c77c4889SXin LI } 305720c436cSXin LI 306720c436cSXin LI init_prompt(); 307720c436cSXin LI 308*c77c4889SXin LI init_unsupport(); 309720c436cSXin LI s = lgetenv(less_is_more ? "MORE" : "LESS"); 310a5f0fb15SPaul Saab if (s != NULL) 31195270f73SXin LI scan_option(s); 312a5f0fb15SPaul Saab 31322249a96SXin LI #define isoptstring(s) less_is_more ? (((s)[0] == '-') && (s)[1] != '\0') : \ 31422249a96SXin LI (((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0') 315a5f0fb15SPaul Saab while (argc > 0 && (isoptstring(*argv) || isoptpending())) 316a5f0fb15SPaul Saab { 317a5f0fb15SPaul Saab s = *argv++; 318a5f0fb15SPaul Saab argc--; 319a5f0fb15SPaul Saab if (strcmp(s, "--") == 0) 320a5f0fb15SPaul Saab break; 321a5f0fb15SPaul Saab scan_option(s); 322a5f0fb15SPaul Saab } 323a5f0fb15SPaul Saab #undef isoptstring 324a5f0fb15SPaul Saab 325a5f0fb15SPaul Saab if (isoptpending()) 326a5f0fb15SPaul Saab { 327a5f0fb15SPaul Saab /* 328a5f0fb15SPaul Saab * Last command line option was a flag requiring a 329a5f0fb15SPaul Saab * following string, but there was no following string. 330a5f0fb15SPaul Saab */ 331a5f0fb15SPaul Saab nopendopt(); 332a5f0fb15SPaul Saab quit(QUIT_OK); 333a5f0fb15SPaul Saab } 334a5f0fb15SPaul Saab 33585901524SXin LI if (less_is_more) 33685901524SXin LI no_init = TRUE; 337720c436cSXin LI 338*c77c4889SXin LI get_term(); 3396f26c71dSXin LI expand_cmd_tables(); 3406f26c71dSXin LI 341a5f0fb15SPaul Saab #if EDITOR 342a5f0fb15SPaul Saab editor = lgetenv("VISUAL"); 343a5f0fb15SPaul Saab if (editor == NULL || *editor == '\0') 344a5f0fb15SPaul Saab { 345a5f0fb15SPaul Saab editor = lgetenv("EDITOR"); 346b7780dbeSXin LI if (isnullenv(editor)) 347a5f0fb15SPaul Saab editor = EDIT_PGM; 348a5f0fb15SPaul Saab } 349a5f0fb15SPaul Saab editproto = lgetenv("LESSEDIT"); 350b7780dbeSXin LI if (isnullenv(editproto)) 351b7780dbeSXin LI editproto = "%E ?lm+%lm. %g"; 352a5f0fb15SPaul Saab #endif 353a5f0fb15SPaul Saab 354a5f0fb15SPaul Saab /* 355a5f0fb15SPaul Saab * Call get_ifile with all the command line filenames 356a5f0fb15SPaul Saab * to "register" them with the ifile system. 357a5f0fb15SPaul Saab */ 358a5f0fb15SPaul Saab ifile = NULL_IFILE; 359a5f0fb15SPaul Saab if (dohelp) 360a5f0fb15SPaul Saab ifile = get_ifile(FAKE_HELPFILE, ifile); 361a5f0fb15SPaul Saab while (argc-- > 0) 362a5f0fb15SPaul Saab { 3638fd4165cSPaul Saab #if (MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC) 364a5f0fb15SPaul Saab /* 365a5f0fb15SPaul Saab * Because the "shell" doesn't expand filename patterns, 366a5f0fb15SPaul Saab * treat each argument as a filename pattern rather than 367a5f0fb15SPaul Saab * a single filename. 368a5f0fb15SPaul Saab * Expand the pattern and iterate over the expanded list. 369a5f0fb15SPaul Saab */ 370a5f0fb15SPaul Saab struct textlist tlist; 371*c77c4889SXin LI constant char *filename; 372a5f0fb15SPaul Saab char *gfilename; 373b2ea2440SXin LI char *qfilename; 374a5f0fb15SPaul Saab 375a5f0fb15SPaul Saab gfilename = lglob(*argv++); 376a5f0fb15SPaul Saab init_textlist(&tlist, gfilename); 377a5f0fb15SPaul Saab filename = NULL; 378a5f0fb15SPaul Saab while ((filename = forw_textlist(&tlist, filename)) != NULL) 3791ede1615STim J. Robbins { 380b2ea2440SXin LI qfilename = shell_unquote(filename); 381b2ea2440SXin LI (void) get_ifile(qfilename, ifile); 382b2ea2440SXin LI free(qfilename); 3831ede1615STim J. Robbins ifile = prev_ifile(NULL_IFILE); 3841ede1615STim J. Robbins } 385a5f0fb15SPaul Saab free(gfilename); 386a5f0fb15SPaul Saab #else 387b2ea2440SXin LI (void) get_ifile(*argv++, ifile); 3881ede1615STim J. Robbins ifile = prev_ifile(NULL_IFILE); 389a5f0fb15SPaul Saab #endif 390a5f0fb15SPaul Saab } 391a5f0fb15SPaul Saab /* 392a5f0fb15SPaul Saab * Set up terminal, etc. 393a5f0fb15SPaul Saab */ 394a5f0fb15SPaul Saab if (!is_tty) 395a5f0fb15SPaul Saab { 396a5f0fb15SPaul Saab /* 397a5f0fb15SPaul Saab * Output is not a tty. 398a5f0fb15SPaul Saab * Just copy the input file(s) to output. 399a5f0fb15SPaul Saab */ 4002235c7feSXin LI set_output(1); /* write to stdout */ 401a5f0fb15SPaul Saab SET_BINARY(1); 402b7780dbeSXin LI if (edit_first() == 0) 403a5f0fb15SPaul Saab { 404a5f0fb15SPaul Saab do { 405a5f0fb15SPaul Saab cat_file(); 406a5f0fb15SPaul Saab } while (edit_next(1) == 0); 407a5f0fb15SPaul Saab } 408a5f0fb15SPaul Saab quit(QUIT_OK); 409a5f0fb15SPaul Saab } 410a5f0fb15SPaul Saab 411aaa7bc6cSXin LI if (missing_cap && !know_dumb && !less_is_more) 412a5f0fb15SPaul Saab error("WARNING: terminal is not fully functional", NULL_PARG); 413a5f0fb15SPaul Saab open_getchr(); 4141ede1615STim J. Robbins raw_mode(1); 415a5f0fb15SPaul Saab init_signals(1); 416a5f0fb15SPaul Saab 417a5f0fb15SPaul Saab /* 418a5f0fb15SPaul Saab * Select the first file to examine. 419a5f0fb15SPaul Saab */ 420a5f0fb15SPaul Saab #if TAGS 4218fd4165cSPaul Saab if (tagoption != NULL || strcmp(tags, "-") == 0) 422a5f0fb15SPaul Saab { 423a5f0fb15SPaul Saab /* 424a5f0fb15SPaul Saab * A -t option was given. 425a5f0fb15SPaul Saab * Verify that no filenames were also given. 426a5f0fb15SPaul Saab * Edit the file selected by the "tags" search, 427a5f0fb15SPaul Saab * and search for the proper line in the file. 428a5f0fb15SPaul Saab */ 429a5f0fb15SPaul Saab if (nifile() > 0) 430a5f0fb15SPaul Saab { 431a5f0fb15SPaul Saab error("No filenames allowed with -t option", NULL_PARG); 432a5f0fb15SPaul Saab quit(QUIT_ERROR); 433a5f0fb15SPaul Saab } 434a5f0fb15SPaul Saab findtag(tagoption); 435a5f0fb15SPaul Saab if (edit_tagfile()) /* Edit file which contains the tag */ 436a5f0fb15SPaul Saab quit(QUIT_ERROR); 437a5f0fb15SPaul Saab /* 438a5f0fb15SPaul Saab * Search for the line which contains the tag. 439a5f0fb15SPaul Saab * Set up initial_scrpos so we display that line. 440a5f0fb15SPaul Saab */ 441a5f0fb15SPaul Saab initial_scrpos.pos = tagsearch(); 442a5f0fb15SPaul Saab if (initial_scrpos.pos == NULL_POSITION) 443a5f0fb15SPaul Saab quit(QUIT_ERROR); 444a5f0fb15SPaul Saab initial_scrpos.ln = jump_sline; 445a5f0fb15SPaul Saab } else 446a5f0fb15SPaul Saab #endif 447a5f0fb15SPaul Saab { 448b7780dbeSXin LI if (edit_first()) 449a5f0fb15SPaul Saab quit(QUIT_ERROR); 450b7780dbeSXin LI /* 451b7780dbeSXin LI * See if file fits on one screen to decide whether 452b7780dbeSXin LI * to send terminal init. But don't need this 453b7780dbeSXin LI * if -X (no_init) overrides this (see init()). 454b7780dbeSXin LI */ 455f6b74a7dSXin LI if (quit_if_one_screen) 456f6b74a7dSXin LI { 457b7780dbeSXin LI if (nifile() > 1) /* If more than one file, -F cannot be used */ 458f6b74a7dSXin LI quit_if_one_screen = FALSE; 459b7780dbeSXin LI else if (!no_init) 460b7780dbeSXin LI one_screen = get_one_screen(); 461f6b74a7dSXin LI } 462a5f0fb15SPaul Saab } 463*c77c4889SXin LI if (init_header != NULL) 464*c77c4889SXin LI { 465*c77c4889SXin LI opt_header(TOGGLE, init_header); 466*c77c4889SXin LI free(init_header); 467*c77c4889SXin LI init_header = NULL; 468*c77c4889SXin LI } 469a5f0fb15SPaul Saab 4702235c7feSXin LI if (errmsgs > 0) 4712235c7feSXin LI { 4722235c7feSXin LI /* 4732235c7feSXin LI * We displayed some messages on error output 4742235c7feSXin LI * (file descriptor 2; see flush()). 4752235c7feSXin LI * Before erasing the screen contents, wait for a keystroke. 4762235c7feSXin LI */ 4772235c7feSXin LI less_printf("Press RETURN to continue ", NULL_PARG); 4782235c7feSXin LI get_return(); 4792235c7feSXin LI putchr('\n'); 4802235c7feSXin LI } 4812235c7feSXin LI set_output(1); 482a5f0fb15SPaul Saab init(); 483a5f0fb15SPaul Saab commands(); 484a5f0fb15SPaul Saab quit(QUIT_OK); 485a5f0fb15SPaul Saab /*NOTREACHED*/ 4861ede1615STim J. Robbins return (0); 487a5f0fb15SPaul Saab } 488a5f0fb15SPaul Saab 489a5f0fb15SPaul Saab /* 490a5f0fb15SPaul Saab * Copy a string to a "safe" place 491a5f0fb15SPaul Saab * (that is, to a buffer allocated by calloc). 492a5f0fb15SPaul Saab */ 493*c77c4889SXin LI public char * saven(constant char *s, size_t n) 494*c77c4889SXin LI { 495*c77c4889SXin LI char *p = (char *) ecalloc(n+1, sizeof(char)); 496*c77c4889SXin LI strncpy(p, s, n); 497*c77c4889SXin LI p[n] = '\0'; 498*c77c4889SXin LI return (p); 499*c77c4889SXin LI } 500*c77c4889SXin LI 501d713e089SXin LI public char * save(constant char *s) 502a5f0fb15SPaul Saab { 503*c77c4889SXin LI return saven(s, strlen(s)); 504a5f0fb15SPaul Saab } 505a5f0fb15SPaul Saab 506d713e089SXin LI public void out_of_memory(void) 507d713e089SXin LI { 508d713e089SXin LI error("Cannot allocate memory", NULL_PARG); 509d713e089SXin LI quit(QUIT_ERROR); 510d713e089SXin LI } 511d713e089SXin LI 512a5f0fb15SPaul Saab /* 513a5f0fb15SPaul Saab * Allocate memory. 514a5f0fb15SPaul Saab * Like calloc(), but never returns an error (NULL). 515a5f0fb15SPaul Saab */ 516*c77c4889SXin LI public void * ecalloc(size_t count, size_t size) 517a5f0fb15SPaul Saab { 518d713e089SXin LI void * p; 519a5f0fb15SPaul Saab 520d713e089SXin LI p = (void *) calloc(count, size); 521d713e089SXin LI if (p == NULL) 522d713e089SXin LI out_of_memory(); 523d713e089SXin LI return p; 524a5f0fb15SPaul Saab } 525a5f0fb15SPaul Saab 526a5f0fb15SPaul Saab /* 527a5f0fb15SPaul Saab * Skip leading spaces in a string. 528a5f0fb15SPaul Saab */ 529d713e089SXin LI public char * skipsp(char *s) 530a5f0fb15SPaul Saab { 531a5f0fb15SPaul Saab while (*s == ' ' || *s == '\t') 532a5f0fb15SPaul Saab s++; 533a5f0fb15SPaul Saab return (s); 534a5f0fb15SPaul Saab } 535a5f0fb15SPaul Saab 536*c77c4889SXin LI /* {{ There must be a better way. }} */ 537*c77c4889SXin LI public constant char * skipspc(constant char *s) 538*c77c4889SXin LI { 539*c77c4889SXin LI while (*s == ' ' || *s == '\t') 540*c77c4889SXin LI s++; 541*c77c4889SXin LI return (s); 542*c77c4889SXin LI } 543*c77c4889SXin LI 544a5f0fb15SPaul Saab /* 545a5f0fb15SPaul Saab * See how many characters of two strings are identical. 546a5f0fb15SPaul Saab * If uppercase is true, the first string must begin with an uppercase 547a5f0fb15SPaul Saab * character; the remainder of the first string may be either case. 548a5f0fb15SPaul Saab */ 549*c77c4889SXin LI public size_t sprefix(constant char *ps, constant char *s, int uppercase) 550a5f0fb15SPaul Saab { 551*c77c4889SXin LI char c; 552*c77c4889SXin LI char sc; 553*c77c4889SXin LI size_t len = 0; 554a5f0fb15SPaul Saab 555a5f0fb15SPaul Saab for ( ; *s != '\0'; s++, ps++) 556a5f0fb15SPaul Saab { 557a5f0fb15SPaul Saab c = *ps; 558a5f0fb15SPaul Saab if (uppercase) 559a5f0fb15SPaul Saab { 56089dd99dcSXin LI if (len == 0 && ASCII_IS_LOWER(c)) 561*c77c4889SXin LI return (0); 56289dd99dcSXin LI if (ASCII_IS_UPPER(c)) 56389dd99dcSXin LI c = ASCII_TO_LOWER(c); 564a5f0fb15SPaul Saab } 565a5f0fb15SPaul Saab sc = *s; 56689dd99dcSXin LI if (len > 0 && ASCII_IS_UPPER(sc)) 56789dd99dcSXin LI sc = ASCII_TO_LOWER(sc); 568a5f0fb15SPaul Saab if (c != sc) 569a5f0fb15SPaul Saab break; 570a5f0fb15SPaul Saab len++; 571a5f0fb15SPaul Saab } 572a5f0fb15SPaul Saab return (len); 573a5f0fb15SPaul Saab } 574a5f0fb15SPaul Saab 575a5f0fb15SPaul Saab /* 576a5f0fb15SPaul Saab * Exit the program. 577a5f0fb15SPaul Saab */ 578d713e089SXin LI public void quit(int status) 579a5f0fb15SPaul Saab { 580a5f0fb15SPaul Saab static int save_status; 581a5f0fb15SPaul Saab 582a5f0fb15SPaul Saab /* 583a5f0fb15SPaul Saab * Put cursor at bottom left corner, clear the line, 584a5f0fb15SPaul Saab * reset the terminal modes, and exit. 585a5f0fb15SPaul Saab */ 586a5f0fb15SPaul Saab if (status < 0) 587a5f0fb15SPaul Saab status = save_status; 588a5f0fb15SPaul Saab else 589a5f0fb15SPaul Saab save_status = status; 590a5f0fb15SPaul Saab quitting = 1; 591d713e089SXin LI check_altpipe_error(); 5922235c7feSXin LI if (interactive()) 593a5f0fb15SPaul Saab clear_bot(); 594a5f0fb15SPaul Saab deinit(); 595a5f0fb15SPaul Saab flush(); 59695270f73SXin LI if (redraw_on_quit && term_init_done) 59795270f73SXin LI { 59895270f73SXin LI /* 59995270f73SXin LI * The last file text displayed might have been on an 60095270f73SXin LI * alternate screen, which now (since deinit) cannot be seen. 60195270f73SXin LI * redraw_on_quit tells us to redraw it on the main screen. 60295270f73SXin LI */ 60395270f73SXin LI first_time = 1; /* Don't print "skipping" or tildes */ 60495270f73SXin LI repaint(); 60595270f73SXin LI flush(); 60695270f73SXin LI } 60795270f73SXin LI edit((char*)NULL); 60895270f73SXin LI save_cmdhist(); 609a5f0fb15SPaul Saab raw_mode(0); 610a5f0fb15SPaul Saab #if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC 611a5f0fb15SPaul Saab /* 612a5f0fb15SPaul Saab * If we don't close 2, we get some garbage from 613a5f0fb15SPaul Saab * 2's buffer when it flushes automatically. 614a5f0fb15SPaul Saab * I cannot track this one down RB 615a5f0fb15SPaul Saab * The same bug shows up if we use ^C^C to abort. 616a5f0fb15SPaul Saab */ 617a5f0fb15SPaul Saab close(2); 618a5f0fb15SPaul Saab #endif 61933096f16SXin LI #ifdef WIN32 620*c77c4889SXin LI SetConsoleTitleW(consoleTitle); 6218fd4165cSPaul Saab #endif 622a5f0fb15SPaul Saab close_getchr(); 623a5f0fb15SPaul Saab exit(status); 624a5f0fb15SPaul Saab } 625*c77c4889SXin LI 626*c77c4889SXin LI /* 627*c77c4889SXin LI * Are all the features in the features mask allowed by security? 628*c77c4889SXin LI */ 629*c77c4889SXin LI public int secure_allow(int features) 630*c77c4889SXin LI { 631*c77c4889SXin LI return ((secure_allow_features & features) == features); 632*c77c4889SXin LI } 633