main.c revision 1.1.1.3 1 1.1 tron /*
2 1.1.1.3 simonb * Copyright (C) 1984-2023 Mark Nudelman
3 1.1 tron *
4 1.1 tron * You may distribute under the terms of either the GNU General Public
5 1.1 tron * License or the Less License, as specified in the README file.
6 1.1 tron *
7 1.1.1.2 tron * For more information, see the README file.
8 1.1 tron */
9 1.1 tron
10 1.1 tron
11 1.1 tron /*
12 1.1 tron * Entry point, initialization, miscellaneous routines.
13 1.1 tron */
14 1.1 tron
15 1.1 tron #include "less.h"
16 1.1 tron #if MSDOS_COMPILER==WIN32C
17 1.1.1.3 simonb #define WIN32_LEAN_AND_MEAN
18 1.1 tron #include <windows.h>
19 1.1 tron #endif
20 1.1 tron
21 1.1.1.3 simonb public char * every_first_cmd = NULL;
22 1.1.1.3 simonb public int new_file;
23 1.1.1.3 simonb public int is_tty;
24 1.1.1.3 simonb public IFILE curr_ifile = NULL_IFILE;
25 1.1.1.3 simonb public IFILE old_ifile = NULL_IFILE;
26 1.1 tron public struct scrpos initial_scrpos;
27 1.1.1.3 simonb public POSITION start_attnpos = NULL_POSITION;
28 1.1.1.3 simonb public POSITION end_attnpos = NULL_POSITION;
29 1.1.1.3 simonb public int wscroll;
30 1.1.1.3 simonb public char * progname;
31 1.1.1.3 simonb public int quitting;
32 1.1.1.3 simonb public int secure;
33 1.1.1.3 simonb public int dohelp;
34 1.1 tron
35 1.1 tron #if LOGFILE
36 1.1.1.3 simonb public int logfile = -1;
37 1.1.1.3 simonb public int force_logfile = FALSE;
38 1.1.1.3 simonb public char * namelogfile = NULL;
39 1.1 tron #endif
40 1.1 tron
41 1.1 tron #if EDITOR
42 1.1.1.3 simonb public char * editor;
43 1.1.1.3 simonb public char * editproto;
44 1.1 tron #endif
45 1.1 tron
46 1.1 tron #if TAGS
47 1.1.1.3 simonb extern char * tags;
48 1.1.1.3 simonb extern char * tagoption;
49 1.1.1.3 simonb extern int jump_sline;
50 1.1 tron #endif
51 1.1 tron
52 1.1 tron #ifdef WIN32
53 1.1 tron static char consoleTitle[256];
54 1.1 tron #endif
55 1.1 tron
56 1.1.1.3 simonb public int one_screen;
57 1.1.1.3 simonb extern int less_is_more;
58 1.1.1.3 simonb extern int missing_cap;
59 1.1.1.3 simonb extern int know_dumb;
60 1.1.1.3 simonb extern int pr_type;
61 1.1.1.3 simonb extern int quit_if_one_screen;
62 1.1.1.3 simonb extern int no_init;
63 1.1.1.3 simonb extern int errmsgs;
64 1.1.1.3 simonb extern int redraw_on_quit;
65 1.1.1.3 simonb extern int term_init_done;
66 1.1.1.3 simonb extern int first_time;
67 1.1 tron
68 1.1 tron /*
69 1.1 tron * Entry point.
70 1.1 tron */
71 1.1.1.3 simonb int main(int argc, char *argv[])
72 1.1 tron {
73 1.1 tron IFILE ifile;
74 1.1 tron char *s;
75 1.1 tron
76 1.1 tron #ifdef __EMX__
77 1.1 tron _response(&argc, &argv);
78 1.1 tron _wildcard(&argc, &argv);
79 1.1 tron #endif
80 1.1 tron
81 1.1 tron progname = *argv++;
82 1.1 tron argc--;
83 1.1 tron
84 1.1.1.3 simonb #if SECURE
85 1.1.1.3 simonb secure = 1;
86 1.1.1.3 simonb #else
87 1.1 tron secure = 0;
88 1.1 tron s = lgetenv("LESSSECURE");
89 1.1.1.3 simonb if (!isnullenv(s))
90 1.1 tron secure = 1;
91 1.1.1.3 simonb #endif
92 1.1 tron
93 1.1 tron #ifdef WIN32
94 1.1 tron if (getenv("HOME") == NULL)
95 1.1 tron {
96 1.1 tron /*
97 1.1 tron * If there is no HOME environment variable,
98 1.1 tron * try the concatenation of HOMEDRIVE + HOMEPATH.
99 1.1 tron */
100 1.1 tron char *drive = getenv("HOMEDRIVE");
101 1.1 tron char *path = getenv("HOMEPATH");
102 1.1 tron if (drive != NULL && path != NULL)
103 1.1 tron {
104 1.1 tron char *env = (char *) ecalloc(strlen(drive) +
105 1.1 tron strlen(path) + 6, sizeof(char));
106 1.1 tron strcpy(env, "HOME=");
107 1.1 tron strcat(env, drive);
108 1.1 tron strcat(env, path);
109 1.1 tron putenv(env);
110 1.1 tron }
111 1.1 tron }
112 1.1 tron GetConsoleTitle(consoleTitle, sizeof(consoleTitle)/sizeof(char));
113 1.1 tron #endif /* WIN32 */
114 1.1 tron
115 1.1 tron /*
116 1.1 tron * Process command line arguments and LESS environment arguments.
117 1.1 tron * Command line arguments override environment arguments.
118 1.1 tron */
119 1.1 tron is_tty = isatty(1);
120 1.1.1.3 simonb init_mark();
121 1.1 tron init_cmds();
122 1.1.1.3 simonb init_poll();
123 1.1.1.3 simonb get_term();
124 1.1 tron init_charset();
125 1.1 tron init_line();
126 1.1 tron init_cmdhist();
127 1.1 tron init_option();
128 1.1 tron init_search();
129 1.1 tron
130 1.1 tron /*
131 1.1 tron * If the name of the executable program is "more",
132 1.1 tron * act like LESS_IS_MORE is set.
133 1.1 tron */
134 1.1.1.3 simonb s = last_component(progname);
135 1.1 tron if (strcmp(s, "more") == 0)
136 1.1 tron less_is_more = 1;
137 1.1 tron
138 1.1 tron init_prompt();
139 1.1 tron
140 1.1 tron s = lgetenv(less_is_more ? "MORE" : "LESS");
141 1.1 tron if (s != NULL)
142 1.1.1.3 simonb scan_option(s);
143 1.1 tron
144 1.1.1.3 simonb #define isoptstring(s) (((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0')
145 1.1 tron while (argc > 0 && (isoptstring(*argv) || isoptpending()))
146 1.1 tron {
147 1.1 tron s = *argv++;
148 1.1 tron argc--;
149 1.1 tron if (strcmp(s, "--") == 0)
150 1.1 tron break;
151 1.1 tron scan_option(s);
152 1.1 tron }
153 1.1 tron #undef isoptstring
154 1.1 tron
155 1.1 tron if (isoptpending())
156 1.1 tron {
157 1.1 tron /*
158 1.1 tron * Last command line option was a flag requiring a
159 1.1 tron * following string, but there was no following string.
160 1.1 tron */
161 1.1 tron nopendopt();
162 1.1 tron quit(QUIT_OK);
163 1.1 tron }
164 1.1 tron
165 1.1.1.3 simonb expand_cmd_tables();
166 1.1 tron
167 1.1 tron #if EDITOR
168 1.1 tron editor = lgetenv("VISUAL");
169 1.1 tron if (editor == NULL || *editor == '\0')
170 1.1 tron {
171 1.1 tron editor = lgetenv("EDITOR");
172 1.1.1.3 simonb if (isnullenv(editor))
173 1.1 tron editor = EDIT_PGM;
174 1.1 tron }
175 1.1 tron editproto = lgetenv("LESSEDIT");
176 1.1.1.3 simonb if (isnullenv(editproto))
177 1.1.1.3 simonb editproto = "%E ?lm+%lm. %g";
178 1.1 tron #endif
179 1.1 tron
180 1.1 tron /*
181 1.1 tron * Call get_ifile with all the command line filenames
182 1.1 tron * to "register" them with the ifile system.
183 1.1 tron */
184 1.1 tron ifile = NULL_IFILE;
185 1.1 tron if (dohelp)
186 1.1 tron ifile = get_ifile(FAKE_HELPFILE, ifile);
187 1.1 tron while (argc-- > 0)
188 1.1 tron {
189 1.1 tron #if (MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC)
190 1.1 tron /*
191 1.1 tron * Because the "shell" doesn't expand filename patterns,
192 1.1 tron * treat each argument as a filename pattern rather than
193 1.1 tron * a single filename.
194 1.1 tron * Expand the pattern and iterate over the expanded list.
195 1.1 tron */
196 1.1 tron struct textlist tlist;
197 1.1.1.3 simonb char *filename;
198 1.1 tron char *gfilename;
199 1.1.1.3 simonb char *qfilename;
200 1.1 tron
201 1.1 tron gfilename = lglob(*argv++);
202 1.1 tron init_textlist(&tlist, gfilename);
203 1.1 tron filename = NULL;
204 1.1 tron while ((filename = forw_textlist(&tlist, filename)) != NULL)
205 1.1 tron {
206 1.1.1.3 simonb qfilename = shell_unquote(filename);
207 1.1.1.3 simonb (void) get_ifile(qfilename, ifile);
208 1.1.1.3 simonb free(qfilename);
209 1.1 tron ifile = prev_ifile(NULL_IFILE);
210 1.1 tron }
211 1.1 tron free(gfilename);
212 1.1 tron #else
213 1.1.1.3 simonb (void) get_ifile(*argv++, ifile);
214 1.1 tron ifile = prev_ifile(NULL_IFILE);
215 1.1 tron #endif
216 1.1 tron }
217 1.1 tron /*
218 1.1 tron * Set up terminal, etc.
219 1.1 tron */
220 1.1 tron if (!is_tty)
221 1.1 tron {
222 1.1 tron /*
223 1.1 tron * Output is not a tty.
224 1.1 tron * Just copy the input file(s) to output.
225 1.1 tron */
226 1.1.1.3 simonb set_output(1); /* write to stdout */
227 1.1 tron SET_BINARY(1);
228 1.1.1.3 simonb if (edit_first() == 0)
229 1.1 tron {
230 1.1 tron do {
231 1.1 tron cat_file();
232 1.1 tron } while (edit_next(1) == 0);
233 1.1 tron }
234 1.1 tron quit(QUIT_OK);
235 1.1 tron }
236 1.1 tron
237 1.1 tron if (missing_cap && !know_dumb)
238 1.1 tron error("WARNING: terminal is not fully functional", NULL_PARG);
239 1.1 tron open_getchr();
240 1.1 tron raw_mode(1);
241 1.1 tron init_signals(1);
242 1.1 tron
243 1.1 tron /*
244 1.1 tron * Select the first file to examine.
245 1.1 tron */
246 1.1 tron #if TAGS
247 1.1 tron if (tagoption != NULL || strcmp(tags, "-") == 0)
248 1.1 tron {
249 1.1 tron /*
250 1.1 tron * A -t option was given.
251 1.1 tron * Verify that no filenames were also given.
252 1.1 tron * Edit the file selected by the "tags" search,
253 1.1 tron * and search for the proper line in the file.
254 1.1 tron */
255 1.1 tron if (nifile() > 0)
256 1.1 tron {
257 1.1 tron error("No filenames allowed with -t option", NULL_PARG);
258 1.1 tron quit(QUIT_ERROR);
259 1.1 tron }
260 1.1 tron findtag(tagoption);
261 1.1 tron if (edit_tagfile()) /* Edit file which contains the tag */
262 1.1 tron quit(QUIT_ERROR);
263 1.1 tron /*
264 1.1 tron * Search for the line which contains the tag.
265 1.1 tron * Set up initial_scrpos so we display that line.
266 1.1 tron */
267 1.1 tron initial_scrpos.pos = tagsearch();
268 1.1 tron if (initial_scrpos.pos == NULL_POSITION)
269 1.1 tron quit(QUIT_ERROR);
270 1.1 tron initial_scrpos.ln = jump_sline;
271 1.1 tron } else
272 1.1 tron #endif
273 1.1 tron {
274 1.1.1.3 simonb if (edit_first())
275 1.1 tron quit(QUIT_ERROR);
276 1.1.1.3 simonb /*
277 1.1.1.3 simonb * See if file fits on one screen to decide whether
278 1.1.1.3 simonb * to send terminal init. But don't need this
279 1.1.1.3 simonb * if -X (no_init) overrides this (see init()).
280 1.1.1.3 simonb */
281 1.1.1.3 simonb if (quit_if_one_screen)
282 1.1.1.3 simonb {
283 1.1.1.3 simonb if (nifile() > 1) /* If more than one file, -F cannot be used */
284 1.1.1.3 simonb quit_if_one_screen = FALSE;
285 1.1.1.3 simonb else if (!no_init)
286 1.1.1.3 simonb one_screen = get_one_screen();
287 1.1.1.3 simonb }
288 1.1 tron }
289 1.1 tron
290 1.1.1.3 simonb if (errmsgs > 0)
291 1.1.1.3 simonb {
292 1.1.1.3 simonb /*
293 1.1.1.3 simonb * We displayed some messages on error output
294 1.1.1.3 simonb * (file descriptor 2; see flush()).
295 1.1.1.3 simonb * Before erasing the screen contents, wait for a keystroke.
296 1.1.1.3 simonb */
297 1.1.1.3 simonb less_printf("Press RETURN to continue ", NULL_PARG);
298 1.1.1.3 simonb get_return();
299 1.1.1.3 simonb putchr('\n');
300 1.1.1.3 simonb }
301 1.1.1.3 simonb set_output(1);
302 1.1 tron init();
303 1.1 tron commands();
304 1.1 tron quit(QUIT_OK);
305 1.1 tron /*NOTREACHED*/
306 1.1 tron return (0);
307 1.1 tron }
308 1.1 tron
309 1.1 tron /*
310 1.1 tron * Copy a string to a "safe" place
311 1.1 tron * (that is, to a buffer allocated by calloc).
312 1.1 tron */
313 1.1.1.3 simonb public char * save(constant char *s)
314 1.1 tron {
315 1.1.1.3 simonb char *p;
316 1.1 tron
317 1.1 tron p = (char *) ecalloc(strlen(s)+1, sizeof(char));
318 1.1 tron strcpy(p, s);
319 1.1 tron return (p);
320 1.1 tron }
321 1.1 tron
322 1.1.1.3 simonb public void out_of_memory(void)
323 1.1.1.3 simonb {
324 1.1.1.3 simonb error("Cannot allocate memory", NULL_PARG);
325 1.1.1.3 simonb quit(QUIT_ERROR);
326 1.1.1.3 simonb }
327 1.1.1.3 simonb
328 1.1 tron /*
329 1.1 tron * Allocate memory.
330 1.1 tron * Like calloc(), but never returns an error (NULL).
331 1.1 tron */
332 1.1.1.3 simonb public void * ecalloc(int count, unsigned int size)
333 1.1 tron {
334 1.1.1.3 simonb void * p;
335 1.1 tron
336 1.1.1.3 simonb p = (void *) calloc(count, size);
337 1.1.1.3 simonb if (p == NULL)
338 1.1.1.3 simonb out_of_memory();
339 1.1.1.3 simonb return p;
340 1.1 tron }
341 1.1 tron
342 1.1 tron /*
343 1.1 tron * Skip leading spaces in a string.
344 1.1 tron */
345 1.1.1.3 simonb public char * skipsp(char *s)
346 1.1 tron {
347 1.1.1.3 simonb while (*s == ' ' || *s == '\t')
348 1.1 tron s++;
349 1.1 tron return (s);
350 1.1 tron }
351 1.1 tron
352 1.1 tron /*
353 1.1 tron * See how many characters of two strings are identical.
354 1.1 tron * If uppercase is true, the first string must begin with an uppercase
355 1.1 tron * character; the remainder of the first string may be either case.
356 1.1 tron */
357 1.1.1.3 simonb public int sprefix(char *ps, char *s, int uppercase)
358 1.1 tron {
359 1.1.1.3 simonb int c;
360 1.1.1.3 simonb int sc;
361 1.1.1.3 simonb int len = 0;
362 1.1 tron
363 1.1 tron for ( ; *s != '\0'; s++, ps++)
364 1.1 tron {
365 1.1 tron c = *ps;
366 1.1 tron if (uppercase)
367 1.1 tron {
368 1.1 tron if (len == 0 && ASCII_IS_LOWER(c))
369 1.1 tron return (-1);
370 1.1 tron if (ASCII_IS_UPPER(c))
371 1.1 tron c = ASCII_TO_LOWER(c);
372 1.1 tron }
373 1.1 tron sc = *s;
374 1.1 tron if (len > 0 && ASCII_IS_UPPER(sc))
375 1.1 tron sc = ASCII_TO_LOWER(sc);
376 1.1 tron if (c != sc)
377 1.1 tron break;
378 1.1 tron len++;
379 1.1 tron }
380 1.1 tron return (len);
381 1.1 tron }
382 1.1 tron
383 1.1 tron /*
384 1.1 tron * Exit the program.
385 1.1 tron */
386 1.1.1.3 simonb public void quit(int status)
387 1.1 tron {
388 1.1 tron static int save_status;
389 1.1 tron
390 1.1 tron /*
391 1.1 tron * Put cursor at bottom left corner, clear the line,
392 1.1 tron * reset the terminal modes, and exit.
393 1.1 tron */
394 1.1 tron if (status < 0)
395 1.1 tron status = save_status;
396 1.1 tron else
397 1.1 tron save_status = status;
398 1.1 tron quitting = 1;
399 1.1.1.3 simonb check_altpipe_error();
400 1.1.1.3 simonb if (interactive())
401 1.1 tron clear_bot();
402 1.1 tron deinit();
403 1.1 tron flush();
404 1.1.1.3 simonb if (redraw_on_quit && term_init_done)
405 1.1.1.3 simonb {
406 1.1.1.3 simonb /*
407 1.1.1.3 simonb * The last file text displayed might have been on an
408 1.1.1.3 simonb * alternate screen, which now (since deinit) cannot be seen.
409 1.1.1.3 simonb * redraw_on_quit tells us to redraw it on the main screen.
410 1.1.1.3 simonb */
411 1.1.1.3 simonb first_time = 1; /* Don't print "skipping" or tildes */
412 1.1.1.3 simonb repaint();
413 1.1.1.3 simonb flush();
414 1.1.1.3 simonb }
415 1.1.1.3 simonb edit((char*)NULL);
416 1.1.1.3 simonb save_cmdhist();
417 1.1 tron raw_mode(0);
418 1.1 tron #if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
419 1.1 tron /*
420 1.1 tron * If we don't close 2, we get some garbage from
421 1.1 tron * 2's buffer when it flushes automatically.
422 1.1 tron * I cannot track this one down RB
423 1.1 tron * The same bug shows up if we use ^C^C to abort.
424 1.1 tron */
425 1.1 tron close(2);
426 1.1 tron #endif
427 1.1 tron #ifdef WIN32
428 1.1 tron SetConsoleTitle(consoleTitle);
429 1.1 tron #endif
430 1.1 tron close_getchr();
431 1.1 tron exit(status);
432 1.1 tron }
433