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