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