1 /*
2 * Copyright (C) 1984-2012 Mark Nudelman
3 * Modified for use with illumos by Garrett D'Amore.
4 * Copyright 2014 Garrett D'Amore <garrett@damore.org>
5 *
6 * You may distribute under the terms of either the GNU General Public
7 * License or the Less License, as specified in the README file.
8 *
9 * For more information, see the README file.
10 */
11
12 /*
13 * Entry point, initialization, miscellaneous routines.
14 */
15
16 #include <sys/types.h>
17
18 #include <libgen.h>
19 #include <stdarg.h>
20
21 #include "less.h"
22
23 char *every_first_cmd = NULL;
24 int new_file;
25 int is_tty;
26 IFILE curr_ifile = NULL;
27 IFILE old_ifile = NULL;
28 struct scrpos initial_scrpos;
29 int any_display = FALSE;
30 off_t start_attnpos = -1;
31 off_t end_attnpos = -1;
32 int wscroll;
33
34 static char *progname;
35
36 int quitting;
37 int secure;
38 int dohelp;
39
40 int logfile = -1;
41 int force_logfile = FALSE;
42 char *namelogfile = NULL;
43 char *editor;
44 char *editproto;
45
46 extern char *tags;
47 extern char *tagoption;
48 extern int jump_sline;
49 extern int less_is_more;
50 extern int missing_cap;
51 extern int know_dumb;
52 extern int quit_if_one_screen;
53 extern int quit_at_eof;
54 extern int pr_type;
55 extern int hilite_search;
56 extern int no_init;
57 extern int top_scroll;
58 extern int errmsgs;
59
60
61 /*
62 * Entry point.
63 */
64 int
main(int argc,char * argv[])65 main(int argc, char *argv[])
66 {
67 IFILE ifile;
68 char *s;
69
70 progname = basename(argv[0]);
71 argv++;
72 argc--;
73
74 /*
75 * If the name of the executable program is "more",
76 * act like LESS_IS_MORE is set. We have to set this as early
77 * as possible for POSIX.
78 */
79 if (strcmp(progname, "more") == 0)
80 less_is_more = 1;
81 else {
82 s = lgetenv("LESS_IS_MORE");
83 if (s != NULL && *s != '\0')
84 less_is_more = 1;
85 }
86
87 secure = 0;
88 s = lgetenv("LESSSECURE");
89 if (s != NULL && *s != '\0')
90 secure = 1;
91
92 if (secure) {
93 if (pledge("stdio rpath tty", NULL) == -1) {
94 perror("pledge");
95 exit(1);
96 }
97 } else {
98 if (pledge("stdio rpath wpath cpath fattr proc exec tty", NULL) == -1) {
99 perror("pledge");
100 exit(1);
101 }
102 }
103
104 /*
105 * Process command line arguments and LESS environment arguments.
106 * Command line arguments override environment arguments.
107 */
108 is_tty = isatty(1);
109 get_term();
110 init_cmds();
111 init_charset();
112 init_line();
113 init_cmdhist();
114 init_option();
115 init_search();
116
117
118 init_prompt();
119
120 if (less_is_more) {
121 /* this is specified by XPG */
122 quit_at_eof = OPT_ON;
123
124 /* more users don't like the warning */
125 know_dumb = OPT_ON;
126
127 /* default prompt is medium */
128 pr_type = OPT_ON;
129
130 /* do not highlight search terms */
131 hilite_search = OPT_OFF;
132
133 /* do not set init strings to terminal */
134 no_init = OPT_ON;
135
136 /* repaint from top of screen */
137 top_scroll = OPT_OFF;
138 }
139
140 s = lgetenv(less_is_more ? "MORE" : "LESS");
141 if (s != NULL)
142 scan_option(estrdup(s), 1);
143
144 #define isoptstring(s) (((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0')
145 while (argc > 0 && (isoptstring(*argv) || isoptpending())) {
146 s = *argv++;
147 argc--;
148 if (strcmp(s, "--") == 0)
149 break;
150 scan_option(s, 0);
151 }
152 #undef isoptstring
153
154 if (isoptpending()) {
155 /*
156 * Last command line option was a flag requiring a
157 * following string, but there was no following string.
158 */
159 nopendopt();
160 quit(QUIT_OK);
161 }
162
163 if (errmsgs) {
164 quit(QUIT_ERROR);
165 }
166 if (less_is_more && quit_at_eof == OPT_ONPLUS) {
167 extern int no_init;
168 no_init = OPT_ON;
169 }
170 if (less_is_more && pr_type == OPT_ONPLUS) {
171 extern int quiet;
172 quiet = VERY_QUIET;
173 }
174
175 editor = lgetenv("VISUAL");
176 if (editor == NULL || *editor == '\0') {
177 editor = lgetenv("EDITOR");
178 if (editor == NULL || *editor == '\0')
179 editor = EDIT_PGM;
180 }
181 editproto = lgetenv("LESSEDIT");
182 if (editproto == NULL || *editproto == '\0')
183 editproto = "%E ?lm+%lm. %f";
184
185 /*
186 * Call get_ifile with all the command line filenames
187 * to "register" them with the ifile system.
188 */
189 ifile = NULL;
190 if (dohelp)
191 ifile = get_ifile(helpfile(), ifile);
192 while (argc-- > 0) {
193 char *filename;
194 filename = shell_quote(*argv);
195 if (filename == NULL)
196 filename = *argv;
197 argv++;
198 (void) get_ifile(filename, ifile);
199 ifile = prev_ifile(NULL);
200 free(filename);
201 }
202 /*
203 * Set up terminal, etc.
204 */
205 if (!is_tty) {
206 /*
207 * Output is not a tty.
208 * Just copy the input file(s) to output.
209 */
210 if (nifile() == 0) {
211 if (edit_stdin() == 0)
212 cat_file();
213 } else if (edit_first() == 0) {
214 do {
215 cat_file();
216 } while (edit_next(1) == 0);
217 }
218 quit(QUIT_OK);
219 }
220
221 if (missing_cap && !know_dumb)
222 error("WARNING: terminal is not fully functional", NULL);
223 init_mark();
224 open_getchr();
225
226 if (secure)
227 if (pledge("stdio rpath tty", NULL) == -1) {
228 perror("pledge");
229 exit(1);
230 }
231
232 raw_mode(1);
233 init_signals(1);
234
235 /*
236 * Select the first file to examine.
237 */
238 if (tagoption != NULL || strcmp(tags, "-") == 0) {
239 /*
240 * A -t option was given.
241 * Verify that no filenames were also given.
242 * Edit the file selected by the "tags" search,
243 * and search for the proper line in the file.
244 */
245 if (nifile() > 0) {
246 error("No filenames allowed with -t option", NULL);
247 quit(QUIT_ERROR);
248 }
249 findtag(tagoption);
250 if (edit_tagfile()) /* Edit file which contains the tag */
251 quit(QUIT_ERROR);
252 /*
253 * Search for the line which contains the tag.
254 * Set up initial_scrpos so we display that line.
255 */
256 initial_scrpos.pos = tagsearch();
257 if (initial_scrpos.pos == -1)
258 quit(QUIT_ERROR);
259 initial_scrpos.ln = jump_sline;
260 } else if (nifile() == 0) {
261 if (edit_stdin()) /* Edit standard input */
262 quit(QUIT_ERROR);
263 } else {
264 if (edit_first()) /* Edit first valid file in cmd line */
265 quit(QUIT_ERROR);
266 }
267
268 init();
269 commands();
270 quit(QUIT_OK);
271 return (0);
272 }
273
274 /*
275 * Allocate memory.
276 * Like calloc(), but never returns an error (NULL).
277 */
278 void *
ecalloc(int count,unsigned int size)279 ecalloc(int count, unsigned int size)
280 {
281 void *p;
282
283 p = calloc(count, size);
284 if (p != NULL)
285 return (p);
286 error("Cannot allocate memory", NULL);
287 quit(QUIT_ERROR);
288 return (NULL);
289 }
290
291 char *
easprintf(const char * fmt,...)292 easprintf(const char *fmt, ...)
293 {
294 char *p = NULL;
295 int rv;
296 va_list ap;
297
298 va_start(ap, fmt);
299 rv = vasprintf(&p, fmt, ap);
300 va_end(ap);
301
302 if (rv == -1) {
303 error("Cannot allocate memory", NULL);
304 quit(QUIT_ERROR);
305 }
306 return (p);
307 }
308
309 char *
estrdup(const char * str)310 estrdup(const char *str)
311 {
312 char *n;
313
314 n = strdup(str);
315 if (n == NULL) {
316 error("Cannot allocate memory", NULL);
317 quit(QUIT_ERROR);
318 }
319 return (n);
320 }
321
322 /*
323 * Skip leading spaces in a string.
324 */
325 char *
skipsp(char * s)326 skipsp(char *s)
327 {
328 while (*s == ' ' || *s == '\t')
329 s++;
330 return (s);
331 }
332
333 /*
334 * See how many characters of two strings are identical.
335 * If uppercase is true, the first string must begin with an uppercase
336 * character; the remainder of the first string may be either case.
337 */
338 int
sprefix(char * ps,char * s,int uppercase)339 sprefix(char *ps, char *s, int uppercase)
340 {
341 int c;
342 int sc;
343 int len = 0;
344
345 for (; *s != '\0'; s++, ps++) {
346 c = *ps;
347 if (uppercase) {
348 if (len == 0 && islower(c))
349 return (-1);
350 c = tolower(c);
351 }
352 sc = *s;
353 if (len > 0)
354 sc = tolower(sc);
355 if (c != sc)
356 break;
357 len++;
358 }
359 return (len);
360 }
361
362 /*
363 * Exit the program.
364 */
365 void
quit(int status)366 quit(int status)
367 {
368 static int save_status;
369
370 /*
371 * Put cursor at bottom left corner, clear the line,
372 * reset the terminal modes, and exit.
373 */
374 if (status < 0)
375 status = save_status;
376 else
377 save_status = status;
378 quitting = 1;
379 edit(NULL);
380 if (!secure)
381 save_cmdhist();
382 if (any_display && is_tty)
383 clear_bot();
384 deinit();
385 flush(1);
386 raw_mode(0);
387 exit(status);
388 }
389
390 char *
helpfile(void)391 helpfile(void)
392 {
393 return (less_is_more ? HELPDIR "/more.help" : HELPDIR "/less.help");
394 }
395