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