11133e27eSPeter Avalos /*
2*e433da38SAaron LI * Copyright (C) 1984-2024 Mark Nudelman
31133e27eSPeter Avalos *
41133e27eSPeter Avalos * You may distribute under the terms of either the GNU General Public
51133e27eSPeter Avalos * License or the Less License, as specified in the README file.
61133e27eSPeter Avalos *
7e639dc31SJohn Marino * For more information, see the README file.
81133e27eSPeter Avalos */
91133e27eSPeter Avalos
101133e27eSPeter Avalos
111133e27eSPeter Avalos /*
121133e27eSPeter Avalos * Entry point, initialization, miscellaneous routines.
131133e27eSPeter Avalos */
141133e27eSPeter Avalos
151133e27eSPeter Avalos #include "less.h"
161133e27eSPeter Avalos #if MSDOS_COMPILER==WIN32C
1702d62a0fSDaniel Fojt #define WIN32_LEAN_AND_MEAN
181133e27eSPeter Avalos #include <windows.h>
19*e433da38SAaron LI
20*e433da38SAaron LI #if defined(MINGW) || defined(_MSC_VER)
21*e433da38SAaron LI #include <locale.h>
22*e433da38SAaron LI #include <shellapi.h>
23*e433da38SAaron LI #endif
24*e433da38SAaron LI
25*e433da38SAaron LI public unsigned less_acp = CP_ACP;
261133e27eSPeter Avalos #endif
271133e27eSPeter Avalos
281133e27eSPeter Avalos public char * every_first_cmd = NULL;
29*e433da38SAaron LI public lbool new_file;
301133e27eSPeter Avalos public int is_tty;
311133e27eSPeter Avalos public IFILE curr_ifile = NULL_IFILE;
321133e27eSPeter Avalos public IFILE old_ifile = NULL_IFILE;
331133e27eSPeter Avalos public struct scrpos initial_scrpos;
341133e27eSPeter Avalos public POSITION start_attnpos = NULL_POSITION;
351133e27eSPeter Avalos public POSITION end_attnpos = NULL_POSITION;
361133e27eSPeter Avalos public int wscroll;
37*e433da38SAaron LI public constant char *progname;
381133e27eSPeter Avalos public int quitting;
391133e27eSPeter Avalos public int dohelp;
40*e433da38SAaron LI static int secure_allow_features;
411133e27eSPeter Avalos
421133e27eSPeter Avalos #if LOGFILE
431133e27eSPeter Avalos public int logfile = -1;
44*e433da38SAaron LI public lbool force_logfile = FALSE;
451133e27eSPeter Avalos public char * namelogfile = NULL;
461133e27eSPeter Avalos #endif
471133e27eSPeter Avalos
481133e27eSPeter Avalos #if EDITOR
49*e433da38SAaron LI public constant char * editor;
50*e433da38SAaron LI public constant char * editproto;
511133e27eSPeter Avalos #endif
521133e27eSPeter Avalos
531133e27eSPeter Avalos #if TAGS
541133e27eSPeter Avalos extern char * tags;
551133e27eSPeter Avalos extern char * tagoption;
561133e27eSPeter Avalos extern int jump_sline;
571133e27eSPeter Avalos #endif
581133e27eSPeter Avalos
591133e27eSPeter Avalos #ifdef WIN32
60*e433da38SAaron LI static wchar_t consoleTitle[256];
611133e27eSPeter Avalos #endif
621133e27eSPeter Avalos
6302d62a0fSDaniel Fojt public int one_screen;
6425ce721eSPeter Avalos extern int less_is_more;
651133e27eSPeter Avalos extern int missing_cap;
661133e27eSPeter Avalos extern int know_dumb;
6702d62a0fSDaniel Fojt extern int quit_if_one_screen;
6802d62a0fSDaniel Fojt extern int no_init;
690c7ad07eSAntonio Huete Jimenez extern int errmsgs;
700c7ad07eSAntonio Huete Jimenez extern int redraw_on_quit;
710c7ad07eSAntonio Huete Jimenez extern int term_init_done;
720c7ad07eSAntonio Huete Jimenez extern int first_time;
731133e27eSPeter Avalos
74*e433da38SAaron LI #if MSDOS_COMPILER==WIN32C && (defined(MINGW) || defined(_MSC_VER))
75*e433da38SAaron LI /* malloc'ed 0-terminated utf8 of 0-terminated wide ws, or null on errors */
utf8_from_wide(constant wchar_t * ws)76*e433da38SAaron LI static char *utf8_from_wide(constant wchar_t *ws)
77*e433da38SAaron LI {
78*e433da38SAaron LI char *u8 = NULL;
79*e433da38SAaron LI int n = WideCharToMultiByte(CP_UTF8, 0, ws, -1, NULL, 0, NULL, NULL);
80*e433da38SAaron LI if (n > 0)
81*e433da38SAaron LI {
82*e433da38SAaron LI u8 = ecalloc(n, sizeof(char));
83*e433da38SAaron LI WideCharToMultiByte(CP_UTF8, 0, ws, -1, u8, n, NULL, NULL);
84*e433da38SAaron LI }
85*e433da38SAaron LI return u8;
86*e433da38SAaron LI }
87*e433da38SAaron LI
88*e433da38SAaron LI /*
89*e433da38SAaron LI * similar to using UTF8 manifest to make the ANSI APIs UTF8, but dynamically
90*e433da38SAaron LI * with setlocale. unlike the manifest, argv and environ are already ACP, so
91*e433da38SAaron LI * make them UTF8. Additionally, this affects only the libc/crt API, and so
92*e433da38SAaron LI * e.g. fopen filename becomes UTF-8, but CreateFileA filename remains CP_ACP.
93*e433da38SAaron LI * CP_ACP remains the original codepage - use the dynamic less_acp instead.
94*e433da38SAaron LI * effective on win 10 1803 or later when compiled with ucrt, else no-op.
95*e433da38SAaron LI */
try_utf8_locale(int * pargc,constant char *** pargv)96*e433da38SAaron LI static void try_utf8_locale(int *pargc, constant char ***pargv)
97*e433da38SAaron LI {
98*e433da38SAaron LI char *locale_orig = strdup(setlocale(LC_ALL, NULL));
99*e433da38SAaron LI wchar_t **wargv = NULL, *wenv, *wp;
100*e433da38SAaron LI constant char **u8argv;
101*e433da38SAaron LI char *u8e;
102*e433da38SAaron LI int i, n;
103*e433da38SAaron LI
104*e433da38SAaron LI if (!setlocale(LC_ALL, ".UTF8"))
105*e433da38SAaron LI goto cleanup; /* not win10 1803+ or not ucrt */
106*e433da38SAaron LI
107*e433da38SAaron LI /*
108*e433da38SAaron LI * wargv is before glob expansion. some ucrt builds may expand globs
109*e433da38SAaron LI * before main is entered, so n may be smaller than the original argc.
110*e433da38SAaron LI * that's ok, because later code at main expands globs anyway.
111*e433da38SAaron LI */
112*e433da38SAaron LI wargv = CommandLineToArgvW(GetCommandLineW(), &n);
113*e433da38SAaron LI if (!wargv)
114*e433da38SAaron LI goto bad_args;
115*e433da38SAaron LI
116*e433da38SAaron LI u8argv = (constant char **) ecalloc(n + 1, sizeof(char *));
117*e433da38SAaron LI for (i = 0; i < n; ++i)
118*e433da38SAaron LI {
119*e433da38SAaron LI if (!(u8argv[i] = utf8_from_wide(wargv[i])))
120*e433da38SAaron LI goto bad_args;
121*e433da38SAaron LI }
122*e433da38SAaron LI u8argv[n] = 0;
123*e433da38SAaron LI
124*e433da38SAaron LI less_acp = CP_UTF8;
125*e433da38SAaron LI *pargc = n;
126*e433da38SAaron LI *pargv = u8argv; /* leaked on exit */
127*e433da38SAaron LI
128*e433da38SAaron LI /* convert wide env to utf8 where we can, but don't abort on errors */
129*e433da38SAaron LI if ((wenv = GetEnvironmentStringsW()))
130*e433da38SAaron LI {
131*e433da38SAaron LI for (wp = wenv; *wp; wp += wcslen(wp) + 1)
132*e433da38SAaron LI {
133*e433da38SAaron LI if ((u8e = utf8_from_wide(wp)))
134*e433da38SAaron LI _putenv(u8e);
135*e433da38SAaron LI free(u8e); /* windows putenv makes a copy */
136*e433da38SAaron LI }
137*e433da38SAaron LI FreeEnvironmentStringsW(wenv);
138*e433da38SAaron LI }
139*e433da38SAaron LI
140*e433da38SAaron LI goto cleanup;
141*e433da38SAaron LI
142*e433da38SAaron LI bad_args:
143*e433da38SAaron LI error("WARNING: cannot use unicode arguments", NULL_PARG);
144*e433da38SAaron LI setlocale(LC_ALL, locale_orig);
145*e433da38SAaron LI
146*e433da38SAaron LI cleanup:
147*e433da38SAaron LI free(locale_orig);
148*e433da38SAaron LI LocalFree(wargv);
149*e433da38SAaron LI }
150*e433da38SAaron LI #endif
151*e433da38SAaron LI
security_feature_error(constant char * type,size_t len,constant char * name)152*e433da38SAaron LI static int security_feature_error(constant char *type, size_t len, constant char *name)
153*e433da38SAaron LI {
154*e433da38SAaron LI PARG parg;
155*e433da38SAaron LI size_t msglen = len + strlen(type) + 64;
156*e433da38SAaron LI char *msg = ecalloc(msglen, sizeof(char));
157*e433da38SAaron LI SNPRINTF3(msg, msglen, "LESSSECURE_ALLOW: %s feature name \"%.*s\"", type, (int) len, name);
158*e433da38SAaron LI parg.p_string = msg;
159*e433da38SAaron LI error("%s", &parg);
160*e433da38SAaron LI free(msg);
161*e433da38SAaron LI return 0;
162*e433da38SAaron LI }
163*e433da38SAaron LI
164*e433da38SAaron LI /*
165*e433da38SAaron LI * Return the SF_xxx value of a secure feature given the name of the feature.
166*e433da38SAaron LI */
security_feature(constant char * name,size_t len)167*e433da38SAaron LI static int security_feature(constant char *name, size_t len)
168*e433da38SAaron LI {
169*e433da38SAaron LI struct secure_feature { constant char *name; int sf_value; };
170*e433da38SAaron LI static struct secure_feature features[] = {
171*e433da38SAaron LI { "edit", SF_EDIT },
172*e433da38SAaron LI { "examine", SF_EXAMINE },
173*e433da38SAaron LI { "glob", SF_GLOB },
174*e433da38SAaron LI { "history", SF_HISTORY },
175*e433da38SAaron LI { "lesskey", SF_LESSKEY },
176*e433da38SAaron LI { "lessopen", SF_LESSOPEN },
177*e433da38SAaron LI { "logfile", SF_LOGFILE },
178*e433da38SAaron LI { "osc8", SF_OSC8_OPEN },
179*e433da38SAaron LI { "pipe", SF_PIPE },
180*e433da38SAaron LI { "shell", SF_SHELL },
181*e433da38SAaron LI { "stop", SF_STOP },
182*e433da38SAaron LI { "tags", SF_TAGS },
183*e433da38SAaron LI };
184*e433da38SAaron LI int i;
185*e433da38SAaron LI int match = -1;
186*e433da38SAaron LI
187*e433da38SAaron LI for (i = 0; i < countof(features); i++)
188*e433da38SAaron LI {
189*e433da38SAaron LI if (strncmp(features[i].name, name, len) == 0)
190*e433da38SAaron LI {
191*e433da38SAaron LI if (match >= 0) /* name is ambiguous */
192*e433da38SAaron LI return security_feature_error("ambiguous", len, name);
193*e433da38SAaron LI match = i;
194*e433da38SAaron LI }
195*e433da38SAaron LI }
196*e433da38SAaron LI if (match < 0)
197*e433da38SAaron LI return security_feature_error("invalid", len, name);
198*e433da38SAaron LI return features[match].sf_value;
199*e433da38SAaron LI }
200*e433da38SAaron LI
201*e433da38SAaron LI /*
202*e433da38SAaron LI * Set the secure_allow_features bitmask, which controls
203*e433da38SAaron LI * whether certain secure features are allowed.
204*e433da38SAaron LI */
init_secure(void)205*e433da38SAaron LI static void init_secure(void)
206*e433da38SAaron LI {
207*e433da38SAaron LI #if SECURE
208*e433da38SAaron LI secure_allow_features = 0;
209*e433da38SAaron LI #else
210*e433da38SAaron LI constant char *str = lgetenv("LESSSECURE");
211*e433da38SAaron LI if (isnullenv(str))
212*e433da38SAaron LI secure_allow_features = ~0; /* allow everything */
213*e433da38SAaron LI else
214*e433da38SAaron LI secure_allow_features = 0; /* allow nothing */
215*e433da38SAaron LI
216*e433da38SAaron LI str = lgetenv("LESSSECURE_ALLOW");
217*e433da38SAaron LI if (!isnullenv(str))
218*e433da38SAaron LI {
219*e433da38SAaron LI for (;;)
220*e433da38SAaron LI {
221*e433da38SAaron LI constant char *estr;
222*e433da38SAaron LI while (*str == ' ' || *str == ',') ++str; /* skip leading spaces/commas */
223*e433da38SAaron LI if (*str == '\0') break;
224*e433da38SAaron LI estr = strchr(str, ',');
225*e433da38SAaron LI if (estr == NULL) estr = str + strlen(str);
226*e433da38SAaron LI while (estr > str && estr[-1] == ' ') --estr; /* trim trailing spaces */
227*e433da38SAaron LI secure_allow_features |= security_feature(str, ptr_diff(estr, str));
228*e433da38SAaron LI str = estr;
229*e433da38SAaron LI }
230*e433da38SAaron LI }
231*e433da38SAaron LI #endif
232*e433da38SAaron LI }
233*e433da38SAaron LI
2341133e27eSPeter Avalos /*
2351133e27eSPeter Avalos * Entry point.
2361133e27eSPeter Avalos */
main(int argc,constant char * argv[])237*e433da38SAaron LI int main(int argc, constant char *argv[])
2381133e27eSPeter Avalos {
2391133e27eSPeter Avalos IFILE ifile;
240*e433da38SAaron LI constant char *s;
241*e433da38SAaron LI
242*e433da38SAaron LI #if MSDOS_COMPILER==WIN32C && (defined(MINGW) || defined(_MSC_VER))
243*e433da38SAaron LI if (GetACP() != CP_UTF8) /* not using a UTF-8 manifest */
244*e433da38SAaron LI try_utf8_locale(&argc, &argv);
245*e433da38SAaron LI #endif
2461133e27eSPeter Avalos
2471133e27eSPeter Avalos #ifdef __EMX__
2481133e27eSPeter Avalos _response(&argc, &argv);
2491133e27eSPeter Avalos _wildcard(&argc, &argv);
2501133e27eSPeter Avalos #endif
2511133e27eSPeter Avalos
2521133e27eSPeter Avalos progname = *argv++;
2531133e27eSPeter Avalos argc--;
254*e433da38SAaron LI init_secure();
2551133e27eSPeter Avalos
2561133e27eSPeter Avalos #ifdef WIN32
2571133e27eSPeter Avalos if (getenv("HOME") == NULL)
2581133e27eSPeter Avalos {
2591133e27eSPeter Avalos /*
2601133e27eSPeter Avalos * If there is no HOME environment variable,
2611133e27eSPeter Avalos * try the concatenation of HOMEDRIVE + HOMEPATH.
2621133e27eSPeter Avalos */
2631133e27eSPeter Avalos char *drive = getenv("HOMEDRIVE");
2641133e27eSPeter Avalos char *path = getenv("HOMEPATH");
2651133e27eSPeter Avalos if (drive != NULL && path != NULL)
2661133e27eSPeter Avalos {
2671133e27eSPeter Avalos char *env = (char *) ecalloc(strlen(drive) +
2681133e27eSPeter Avalos strlen(path) + 6, sizeof(char));
2691133e27eSPeter Avalos strcpy(env, "HOME=");
2701133e27eSPeter Avalos strcat(env, drive);
2711133e27eSPeter Avalos strcat(env, path);
2721133e27eSPeter Avalos putenv(env);
2731133e27eSPeter Avalos }
2741133e27eSPeter Avalos }
275*e433da38SAaron LI /* on failure, consoleTitle is already a valid empty string */
276*e433da38SAaron LI GetConsoleTitleW(consoleTitle, countof(consoleTitle));
2771133e27eSPeter Avalos #endif /* WIN32 */
2781133e27eSPeter Avalos
2791133e27eSPeter Avalos /*
2801133e27eSPeter Avalos * Process command line arguments and LESS environment arguments.
2811133e27eSPeter Avalos * Command line arguments override environment arguments.
2821133e27eSPeter Avalos */
2831133e27eSPeter Avalos is_tty = isatty(1);
28402d62a0fSDaniel Fojt init_mark();
2851133e27eSPeter Avalos init_cmds();
286320d7c8aSAaron LI init_poll();
2871133e27eSPeter Avalos init_charset();
2881133e27eSPeter Avalos init_line();
2891133e27eSPeter Avalos init_cmdhist();
2901133e27eSPeter Avalos init_option();
291a9adbba3SJan Lentfer init_search();
2921133e27eSPeter Avalos
2931133e27eSPeter Avalos /*
2941133e27eSPeter Avalos * If the name of the executable program is "more",
2951133e27eSPeter Avalos * act like LESS_IS_MORE is set.
2961133e27eSPeter Avalos */
297*e433da38SAaron LI if (strcmp(last_component(progname), "more") == 0)
2981133e27eSPeter Avalos less_is_more = 1;
2991133e27eSPeter Avalos
3001133e27eSPeter Avalos init_prompt();
3011133e27eSPeter Avalos
302*e433da38SAaron LI init_unsupport();
3031133e27eSPeter Avalos s = lgetenv(less_is_more ? "MORE" : "LESS");
3041133e27eSPeter Avalos if (s != NULL)
3050c7ad07eSAntonio Huete Jimenez scan_option(s);
3061133e27eSPeter Avalos
3071133e27eSPeter Avalos #define isoptstring(s) (((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0')
3081133e27eSPeter Avalos while (argc > 0 && (isoptstring(*argv) || isoptpending()))
3091133e27eSPeter Avalos {
3101133e27eSPeter Avalos s = *argv++;
3111133e27eSPeter Avalos argc--;
3121133e27eSPeter Avalos if (strcmp(s, "--") == 0)
3131133e27eSPeter Avalos break;
3141133e27eSPeter Avalos scan_option(s);
3151133e27eSPeter Avalos }
3161133e27eSPeter Avalos #undef isoptstring
3171133e27eSPeter Avalos
3181133e27eSPeter Avalos if (isoptpending())
3191133e27eSPeter Avalos {
3201133e27eSPeter Avalos /*
3211133e27eSPeter Avalos * Last command line option was a flag requiring a
3221133e27eSPeter Avalos * following string, but there was no following string.
3231133e27eSPeter Avalos */
3241133e27eSPeter Avalos nopendopt();
3251133e27eSPeter Avalos quit(QUIT_OK);
3261133e27eSPeter Avalos }
3271133e27eSPeter Avalos
328*e433da38SAaron LI get_term();
3290c7ad07eSAntonio Huete Jimenez expand_cmd_tables();
3300c7ad07eSAntonio Huete Jimenez
3311133e27eSPeter Avalos #if EDITOR
3321133e27eSPeter Avalos editor = lgetenv("VISUAL");
3331133e27eSPeter Avalos if (editor == NULL || *editor == '\0')
3341133e27eSPeter Avalos {
3351133e27eSPeter Avalos editor = lgetenv("EDITOR");
33602d62a0fSDaniel Fojt if (isnullenv(editor))
3371133e27eSPeter Avalos editor = EDIT_PGM;
3381133e27eSPeter Avalos }
3391133e27eSPeter Avalos editproto = lgetenv("LESSEDIT");
34002d62a0fSDaniel Fojt if (isnullenv(editproto))
34102d62a0fSDaniel Fojt editproto = "%E ?lm+%lm. %g";
3421133e27eSPeter Avalos #endif
3431133e27eSPeter Avalos
3441133e27eSPeter Avalos /*
3451133e27eSPeter Avalos * Call get_ifile with all the command line filenames
3461133e27eSPeter Avalos * to "register" them with the ifile system.
3471133e27eSPeter Avalos */
3481133e27eSPeter Avalos ifile = NULL_IFILE;
3491133e27eSPeter Avalos if (dohelp)
3501133e27eSPeter Avalos ifile = get_ifile(FAKE_HELPFILE, ifile);
3511133e27eSPeter Avalos while (argc-- > 0)
3521133e27eSPeter Avalos {
3531133e27eSPeter Avalos #if (MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC)
3541133e27eSPeter Avalos /*
3551133e27eSPeter Avalos * Because the "shell" doesn't expand filename patterns,
3561133e27eSPeter Avalos * treat each argument as a filename pattern rather than
3571133e27eSPeter Avalos * a single filename.
3581133e27eSPeter Avalos * Expand the pattern and iterate over the expanded list.
3591133e27eSPeter Avalos */
3601133e27eSPeter Avalos struct textlist tlist;
361*e433da38SAaron LI constant char *filename;
3621133e27eSPeter Avalos char *gfilename;
36302d62a0fSDaniel Fojt char *qfilename;
3641133e27eSPeter Avalos
3651133e27eSPeter Avalos gfilename = lglob(*argv++);
3661133e27eSPeter Avalos init_textlist(&tlist, gfilename);
3671133e27eSPeter Avalos filename = NULL;
3681133e27eSPeter Avalos while ((filename = forw_textlist(&tlist, filename)) != NULL)
3691133e27eSPeter Avalos {
37002d62a0fSDaniel Fojt qfilename = shell_unquote(filename);
37102d62a0fSDaniel Fojt (void) get_ifile(qfilename, ifile);
37202d62a0fSDaniel Fojt free(qfilename);
3731133e27eSPeter Avalos ifile = prev_ifile(NULL_IFILE);
3741133e27eSPeter Avalos }
3751133e27eSPeter Avalos free(gfilename);
3761133e27eSPeter Avalos #else
37702d62a0fSDaniel Fojt (void) get_ifile(*argv++, ifile);
3781133e27eSPeter Avalos ifile = prev_ifile(NULL_IFILE);
3791133e27eSPeter Avalos #endif
3801133e27eSPeter Avalos }
3811133e27eSPeter Avalos /*
3821133e27eSPeter Avalos * Set up terminal, etc.
3831133e27eSPeter Avalos */
3841133e27eSPeter Avalos if (!is_tty)
3851133e27eSPeter Avalos {
3861133e27eSPeter Avalos /*
3871133e27eSPeter Avalos * Output is not a tty.
3881133e27eSPeter Avalos * Just copy the input file(s) to output.
3891133e27eSPeter Avalos */
3900c7ad07eSAntonio Huete Jimenez set_output(1); /* write to stdout */
3911133e27eSPeter Avalos SET_BINARY(1);
39202d62a0fSDaniel Fojt if (edit_first() == 0)
3931133e27eSPeter Avalos {
3941133e27eSPeter Avalos do {
3951133e27eSPeter Avalos cat_file();
3961133e27eSPeter Avalos } while (edit_next(1) == 0);
3971133e27eSPeter Avalos }
3981133e27eSPeter Avalos quit(QUIT_OK);
3991133e27eSPeter Avalos }
4001133e27eSPeter Avalos
4011133e27eSPeter Avalos if (missing_cap && !know_dumb)
4021133e27eSPeter Avalos error("WARNING: terminal is not fully functional", NULL_PARG);
4031133e27eSPeter Avalos open_getchr();
4041133e27eSPeter Avalos raw_mode(1);
4051133e27eSPeter Avalos init_signals(1);
4061133e27eSPeter Avalos
4071133e27eSPeter Avalos /*
4081133e27eSPeter Avalos * Select the first file to examine.
4091133e27eSPeter Avalos */
4101133e27eSPeter Avalos #if TAGS
4111133e27eSPeter Avalos if (tagoption != NULL || strcmp(tags, "-") == 0)
4121133e27eSPeter Avalos {
4131133e27eSPeter Avalos /*
4141133e27eSPeter Avalos * A -t option was given.
4151133e27eSPeter Avalos * Verify that no filenames were also given.
4161133e27eSPeter Avalos * Edit the file selected by the "tags" search,
4171133e27eSPeter Avalos * and search for the proper line in the file.
4181133e27eSPeter Avalos */
4191133e27eSPeter Avalos if (nifile() > 0)
4201133e27eSPeter Avalos {
4211133e27eSPeter Avalos error("No filenames allowed with -t option", NULL_PARG);
4221133e27eSPeter Avalos quit(QUIT_ERROR);
4231133e27eSPeter Avalos }
4241133e27eSPeter Avalos findtag(tagoption);
4251133e27eSPeter Avalos if (edit_tagfile()) /* Edit file which contains the tag */
4261133e27eSPeter Avalos quit(QUIT_ERROR);
4271133e27eSPeter Avalos /*
4281133e27eSPeter Avalos * Search for the line which contains the tag.
4291133e27eSPeter Avalos * Set up initial_scrpos so we display that line.
4301133e27eSPeter Avalos */
4311133e27eSPeter Avalos initial_scrpos.pos = tagsearch();
4321133e27eSPeter Avalos if (initial_scrpos.pos == NULL_POSITION)
4331133e27eSPeter Avalos quit(QUIT_ERROR);
4341133e27eSPeter Avalos initial_scrpos.ln = jump_sline;
4351133e27eSPeter Avalos } else
4361133e27eSPeter Avalos #endif
4371133e27eSPeter Avalos {
43802d62a0fSDaniel Fojt if (edit_first())
4391133e27eSPeter Avalos quit(QUIT_ERROR);
44002d62a0fSDaniel Fojt /*
44102d62a0fSDaniel Fojt * See if file fits on one screen to decide whether
44202d62a0fSDaniel Fojt * to send terminal init. But don't need this
44302d62a0fSDaniel Fojt * if -X (no_init) overrides this (see init()).
44402d62a0fSDaniel Fojt */
44502d62a0fSDaniel Fojt if (quit_if_one_screen)
4461133e27eSPeter Avalos {
44702d62a0fSDaniel Fojt if (nifile() > 1) /* If more than one file, -F cannot be used */
44802d62a0fSDaniel Fojt quit_if_one_screen = FALSE;
44902d62a0fSDaniel Fojt else if (!no_init)
45002d62a0fSDaniel Fojt one_screen = get_one_screen();
45102d62a0fSDaniel Fojt }
4521133e27eSPeter Avalos }
4531133e27eSPeter Avalos
4540c7ad07eSAntonio Huete Jimenez if (errmsgs > 0)
4550c7ad07eSAntonio Huete Jimenez {
4560c7ad07eSAntonio Huete Jimenez /*
4570c7ad07eSAntonio Huete Jimenez * We displayed some messages on error output
4580c7ad07eSAntonio Huete Jimenez * (file descriptor 2; see flush()).
4590c7ad07eSAntonio Huete Jimenez * Before erasing the screen contents, wait for a keystroke.
4600c7ad07eSAntonio Huete Jimenez */
4610c7ad07eSAntonio Huete Jimenez less_printf("Press RETURN to continue ", NULL_PARG);
4620c7ad07eSAntonio Huete Jimenez get_return();
4630c7ad07eSAntonio Huete Jimenez putchr('\n');
4640c7ad07eSAntonio Huete Jimenez }
4650c7ad07eSAntonio Huete Jimenez set_output(1);
4661133e27eSPeter Avalos init();
4671133e27eSPeter Avalos commands();
4681133e27eSPeter Avalos quit(QUIT_OK);
4691133e27eSPeter Avalos /*NOTREACHED*/
4701133e27eSPeter Avalos return (0);
4711133e27eSPeter Avalos }
4721133e27eSPeter Avalos
4731133e27eSPeter Avalos /*
4741133e27eSPeter Avalos * Copy a string to a "safe" place
4751133e27eSPeter Avalos * (that is, to a buffer allocated by calloc).
4761133e27eSPeter Avalos */
saven(constant char * s,size_t n)477*e433da38SAaron LI public char * saven(constant char *s, size_t n)
478*e433da38SAaron LI {
479*e433da38SAaron LI char *p = (char *) ecalloc(n+1, sizeof(char));
480*e433da38SAaron LI strncpy(p, s, n);
481*e433da38SAaron LI p[n] = '\0';
482*e433da38SAaron LI return (p);
483*e433da38SAaron LI }
484*e433da38SAaron LI
save(constant char * s)485320d7c8aSAaron LI public char * save(constant char *s)
4861133e27eSPeter Avalos {
487*e433da38SAaron LI return saven(s, strlen(s));
4881133e27eSPeter Avalos }
4891133e27eSPeter Avalos
out_of_memory(void)490320d7c8aSAaron LI public void out_of_memory(void)
491320d7c8aSAaron LI {
492320d7c8aSAaron LI error("Cannot allocate memory", NULL_PARG);
493320d7c8aSAaron LI quit(QUIT_ERROR);
494320d7c8aSAaron LI }
495320d7c8aSAaron LI
4961133e27eSPeter Avalos /*
4971133e27eSPeter Avalos * Allocate memory.
4981133e27eSPeter Avalos * Like calloc(), but never returns an error (NULL).
4991133e27eSPeter Avalos */
ecalloc(size_t count,size_t size)500*e433da38SAaron LI public void * ecalloc(size_t count, size_t size)
5011133e27eSPeter Avalos {
502320d7c8aSAaron LI void * p;
5031133e27eSPeter Avalos
504320d7c8aSAaron LI p = (void *) calloc(count, size);
505320d7c8aSAaron LI if (p == NULL)
506320d7c8aSAaron LI out_of_memory();
507320d7c8aSAaron LI return p;
5081133e27eSPeter Avalos }
5091133e27eSPeter Avalos
5101133e27eSPeter Avalos /*
5111133e27eSPeter Avalos * Skip leading spaces in a string.
5121133e27eSPeter Avalos */
skipsp(char * s)513320d7c8aSAaron LI public char * skipsp(char *s)
5141133e27eSPeter Avalos {
5151133e27eSPeter Avalos while (*s == ' ' || *s == '\t')
5161133e27eSPeter Avalos s++;
5171133e27eSPeter Avalos return (s);
5181133e27eSPeter Avalos }
5191133e27eSPeter Avalos
520*e433da38SAaron LI /* {{ There must be a better way. }} */
skipspc(constant char * s)521*e433da38SAaron LI public constant char * skipspc(constant char *s)
522*e433da38SAaron LI {
523*e433da38SAaron LI while (*s == ' ' || *s == '\t')
524*e433da38SAaron LI s++;
525*e433da38SAaron LI return (s);
526*e433da38SAaron LI }
527*e433da38SAaron LI
5281133e27eSPeter Avalos /*
5291133e27eSPeter Avalos * See how many characters of two strings are identical.
5301133e27eSPeter Avalos * If uppercase is true, the first string must begin with an uppercase
5311133e27eSPeter Avalos * character; the remainder of the first string may be either case.
5321133e27eSPeter Avalos */
sprefix(constant char * ps,constant char * s,int uppercase)533*e433da38SAaron LI public size_t sprefix(constant char *ps, constant char *s, int uppercase)
5341133e27eSPeter Avalos {
535*e433da38SAaron LI char c;
536*e433da38SAaron LI char sc;
537*e433da38SAaron LI size_t len = 0;
5381133e27eSPeter Avalos
5391133e27eSPeter Avalos for ( ; *s != '\0'; s++, ps++)
5401133e27eSPeter Avalos {
5411133e27eSPeter Avalos c = *ps;
5421133e27eSPeter Avalos if (uppercase)
5431133e27eSPeter Avalos {
5441133e27eSPeter Avalos if (len == 0 && ASCII_IS_LOWER(c))
545*e433da38SAaron LI return (0);
5461133e27eSPeter Avalos if (ASCII_IS_UPPER(c))
5471133e27eSPeter Avalos c = ASCII_TO_LOWER(c);
5481133e27eSPeter Avalos }
5491133e27eSPeter Avalos sc = *s;
5501133e27eSPeter Avalos if (len > 0 && ASCII_IS_UPPER(sc))
5511133e27eSPeter Avalos sc = ASCII_TO_LOWER(sc);
5521133e27eSPeter Avalos if (c != sc)
5531133e27eSPeter Avalos break;
5541133e27eSPeter Avalos len++;
5551133e27eSPeter Avalos }
5561133e27eSPeter Avalos return (len);
5571133e27eSPeter Avalos }
5581133e27eSPeter Avalos
5591133e27eSPeter Avalos /*
5601133e27eSPeter Avalos * Exit the program.
5611133e27eSPeter Avalos */
quit(int status)562320d7c8aSAaron LI public void quit(int status)
5631133e27eSPeter Avalos {
5641133e27eSPeter Avalos static int save_status;
5651133e27eSPeter Avalos
5661133e27eSPeter Avalos /*
5671133e27eSPeter Avalos * Put cursor at bottom left corner, clear the line,
5681133e27eSPeter Avalos * reset the terminal modes, and exit.
5691133e27eSPeter Avalos */
5701133e27eSPeter Avalos if (status < 0)
5711133e27eSPeter Avalos status = save_status;
5721133e27eSPeter Avalos else
5731133e27eSPeter Avalos save_status = status;
5741133e27eSPeter Avalos quitting = 1;
575320d7c8aSAaron LI check_altpipe_error();
5760c7ad07eSAntonio Huete Jimenez if (interactive())
5771133e27eSPeter Avalos clear_bot();
5781133e27eSPeter Avalos deinit();
5791133e27eSPeter Avalos flush();
5800c7ad07eSAntonio Huete Jimenez if (redraw_on_quit && term_init_done)
5810c7ad07eSAntonio Huete Jimenez {
5820c7ad07eSAntonio Huete Jimenez /*
5830c7ad07eSAntonio Huete Jimenez * The last file text displayed might have been on an
5840c7ad07eSAntonio Huete Jimenez * alternate screen, which now (since deinit) cannot be seen.
5850c7ad07eSAntonio Huete Jimenez * redraw_on_quit tells us to redraw it on the main screen.
5860c7ad07eSAntonio Huete Jimenez */
5870c7ad07eSAntonio Huete Jimenez first_time = 1; /* Don't print "skipping" or tildes */
5880c7ad07eSAntonio Huete Jimenez repaint();
5890c7ad07eSAntonio Huete Jimenez flush();
5900c7ad07eSAntonio Huete Jimenez }
5910c7ad07eSAntonio Huete Jimenez edit((char*)NULL);
5920c7ad07eSAntonio Huete Jimenez save_cmdhist();
5931133e27eSPeter Avalos raw_mode(0);
5941133e27eSPeter Avalos #if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
5951133e27eSPeter Avalos /*
5961133e27eSPeter Avalos * If we don't close 2, we get some garbage from
5971133e27eSPeter Avalos * 2's buffer when it flushes automatically.
5981133e27eSPeter Avalos * I cannot track this one down RB
5991133e27eSPeter Avalos * The same bug shows up if we use ^C^C to abort.
6001133e27eSPeter Avalos */
6011133e27eSPeter Avalos close(2);
6021133e27eSPeter Avalos #endif
60325ce721eSPeter Avalos #ifdef WIN32
604*e433da38SAaron LI SetConsoleTitleW(consoleTitle);
6051133e27eSPeter Avalos #endif
6061133e27eSPeter Avalos close_getchr();
6071133e27eSPeter Avalos exit(status);
6081133e27eSPeter Avalos }
609*e433da38SAaron LI
610*e433da38SAaron LI /*
611*e433da38SAaron LI * Are all the features in the features mask allowed by security?
612*e433da38SAaron LI */
secure_allow(int features)613*e433da38SAaron LI public int secure_allow(int features)
614*e433da38SAaron LI {
615*e433da38SAaron LI return ((secure_allow_features & features) == features);
616*e433da38SAaron LI }
617