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