1 /* $FreeBSD$ */ 2 /* 3 * Copyright (C) 1984-2007 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 about less, or for information on how to 9 * contact the author, see the README file. 10 */ 11 12 13 /* 14 * Entry point, initialization, miscellaneous routines. 15 */ 16 17 #include "less.h" 18 #if MSDOS_COMPILER==WIN32C 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 int any_display = FALSE; 29 public POSITION start_attnpos = NULL_POSITION; 30 public POSITION end_attnpos = NULL_POSITION; 31 public int wscroll; 32 public char * progname; 33 public int quitting; 34 public int secure; 35 public int dohelp; 36 public int less_is_more; 37 38 #if LOGFILE 39 public int logfile = -1; 40 public int force_logfile = FALSE; 41 public char * namelogfile = NULL; 42 #endif 43 44 #if EDITOR 45 public char * editor; 46 public char * editproto; 47 #endif 48 49 #if TAGS 50 extern char * tags; 51 extern char * tagoption; 52 extern int jump_sline; 53 #endif 54 55 #ifdef WIN32 56 static char consoleTitle[256]; 57 #endif 58 59 extern int missing_cap; 60 extern int know_dumb; 61 extern int quit_if_one_screen; 62 extern int pr_type; 63 64 65 /* 66 * Entry point. 67 */ 68 int 69 main(argc, argv) 70 int argc; 71 char *argv[]; 72 { 73 IFILE ifile; 74 char *s; 75 extern char *__progname; 76 77 #ifdef __EMX__ 78 _response(&argc, &argv); 79 _wildcard(&argc, &argv); 80 #endif 81 82 progname = *argv++; 83 argc--; 84 85 secure = 0; 86 s = lgetenv("LESSSECURE"); 87 if (s != NULL && *s != '\0') 88 secure = 1; 89 90 #ifdef WIN32 91 if (getenv("HOME") == NULL) 92 { 93 /* 94 * If there is no HOME environment variable, 95 * try the concatenation of HOMEDRIVE + HOMEPATH. 96 */ 97 char *drive = getenv("HOMEDRIVE"); 98 char *path = getenv("HOMEPATH"); 99 if (drive != NULL && path != NULL) 100 { 101 char *env = (char *) ecalloc(strlen(drive) + 102 strlen(path) + 6, sizeof(char)); 103 strcpy(env, "HOME="); 104 strcat(env, drive); 105 strcat(env, path); 106 putenv(env); 107 } 108 } 109 GetConsoleTitle(consoleTitle, sizeof(consoleTitle)/sizeof(char)); 110 #endif /* WIN32 */ 111 112 /* 113 * Process command line arguments and LESS environment arguments. 114 * Command line arguments override environment arguments. 115 */ 116 is_tty = isatty(1); 117 get_term(); 118 init_cmds(); 119 init_charset(); 120 init_line(); 121 init_cmdhist(); 122 init_option(); 123 124 /* 125 * If the name of the executable program is "more", 126 * act like LESS_IS_MORE is set. 127 */ 128 for (s = progname + strlen(progname); s > progname; s--) 129 { 130 if (s[-1] == PATHNAME_SEP[0]) 131 break; 132 } 133 if (strcmp(s, "more") == 0) 134 less_is_more = 1; 135 136 init_prompt(); 137 138 s = lgetenv(less_is_more ? "MORE" : "LESS"); 139 if (s != NULL) 140 scan_option(save(s)); 141 142 #define isoptstring(s) (((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0') 143 while (argc > 0 && (isoptstring(*argv) || isoptpending())) 144 { 145 s = *argv++; 146 argc--; 147 if (strcmp(s, "--") == 0) 148 break; 149 scan_option(s); 150 } 151 #undef isoptstring 152 153 if (isoptpending()) 154 { 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 (less_is_more && get_quit_at_eof()) 164 quit_if_one_screen = TRUE; 165 166 #if EDITOR 167 editor = lgetenv("VISUAL"); 168 if (editor == NULL || *editor == '\0') 169 { 170 editor = lgetenv("EDITOR"); 171 if (editor == NULL || *editor == '\0') 172 editor = EDIT_PGM; 173 } 174 editproto = lgetenv("LESSEDIT"); 175 if (editproto == NULL || *editproto == '\0') 176 editproto = "%E ?lm+%lm. %f"; 177 #endif 178 179 /* 180 * Call get_ifile with all the command line filenames 181 * to "register" them with the ifile system. 182 */ 183 ifile = NULL_IFILE; 184 if (dohelp) 185 ifile = get_ifile(FAKE_HELPFILE, ifile); 186 while (argc-- > 0) 187 { 188 char *filename; 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 *gfilename; 198 199 gfilename = lglob(*argv++); 200 init_textlist(&tlist, gfilename); 201 filename = NULL; 202 while ((filename = forw_textlist(&tlist, filename)) != NULL) 203 { 204 (void) get_ifile(filename, ifile); 205 ifile = prev_ifile(NULL_IFILE); 206 } 207 free(gfilename); 208 #else 209 filename = shell_quote(*argv); 210 if (filename == NULL) 211 filename = *argv; 212 argv++; 213 (void) get_ifile(filename, 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_BINARY(1); 227 if (nifile() == 0) 228 { 229 if (edit_stdin() == 0) 230 cat_file(); 231 } else if (edit_first() == 0) 232 { 233 do { 234 cat_file(); 235 } while (edit_next(1) == 0); 236 } 237 quit(QUIT_OK); 238 } 239 240 if (missing_cap && !know_dumb) 241 error("WARNING: terminal is not fully functional", NULL_PARG); 242 init_mark(); 243 open_getchr(); 244 raw_mode(1); 245 init_signals(1); 246 247 /* 248 * Select the first file to examine. 249 */ 250 #if TAGS 251 if (tagoption != NULL || strcmp(tags, "-") == 0) 252 { 253 /* 254 * A -t option was given. 255 * Verify that no filenames were also given. 256 * Edit the file selected by the "tags" search, 257 * and search for the proper line in the file. 258 */ 259 if (nifile() > 0) 260 { 261 error("No filenames allowed with -t option", NULL_PARG); 262 quit(QUIT_ERROR); 263 } 264 findtag(tagoption); 265 if (edit_tagfile()) /* Edit file which contains the tag */ 266 quit(QUIT_ERROR); 267 /* 268 * Search for the line which contains the tag. 269 * Set up initial_scrpos so we display that line. 270 */ 271 initial_scrpos.pos = tagsearch(); 272 if (initial_scrpos.pos == NULL_POSITION) 273 quit(QUIT_ERROR); 274 initial_scrpos.ln = jump_sline; 275 } else 276 #endif 277 if (nifile() == 0) 278 { 279 if (edit_stdin()) /* Edit standard input */ 280 quit(QUIT_ERROR); 281 } else 282 { 283 if (edit_first()) /* Edit first valid file in cmd line */ 284 quit(QUIT_ERROR); 285 } 286 287 init(); 288 commands(); 289 quit(QUIT_OK); 290 /*NOTREACHED*/ 291 return (0); 292 } 293 294 /* 295 * Copy a string to a "safe" place 296 * (that is, to a buffer allocated by calloc). 297 */ 298 public char * 299 save(s) 300 char *s; 301 { 302 register char *p; 303 304 p = (char *) ecalloc(strlen(s)+1, sizeof(char)); 305 strcpy(p, s); 306 return (p); 307 } 308 309 /* 310 * Allocate memory. 311 * Like calloc(), but never returns an error (NULL). 312 */ 313 public VOID_POINTER 314 ecalloc(count, size) 315 int count; 316 unsigned int size; 317 { 318 register VOID_POINTER p; 319 320 p = (VOID_POINTER) calloc(count, size); 321 if (p != NULL) 322 return (p); 323 error("Cannot allocate memory", NULL_PARG); 324 quit(QUIT_ERROR); 325 /*NOTREACHED*/ 326 return (NULL); 327 } 328 329 /* 330 * Skip leading spaces in a string. 331 */ 332 public char * 333 skipsp(s) 334 register char *s; 335 { 336 while (*s == ' ' || *s == '\t') 337 s++; 338 return (s); 339 } 340 341 /* 342 * See how many characters of two strings are identical. 343 * If uppercase is true, the first string must begin with an uppercase 344 * character; the remainder of the first string may be either case. 345 */ 346 public int 347 sprefix(ps, s, uppercase) 348 char *ps; 349 char *s; 350 int uppercase; 351 { 352 register int c; 353 register int sc; 354 register int len = 0; 355 356 for ( ; *s != '\0'; s++, ps++) 357 { 358 c = *ps; 359 if (uppercase) 360 { 361 if (len == 0 && ASCII_IS_LOWER(c)) 362 return (-1); 363 if (ASCII_IS_UPPER(c)) 364 c = ASCII_TO_LOWER(c); 365 } 366 sc = *s; 367 if (len > 0 && ASCII_IS_UPPER(sc)) 368 sc = ASCII_TO_LOWER(sc); 369 if (c != sc) 370 break; 371 len++; 372 } 373 return (len); 374 } 375 376 /* 377 * Exit the program. 378 */ 379 public void 380 quit(status) 381 int status; 382 { 383 static int save_status; 384 385 /* 386 * Put cursor at bottom left corner, clear the line, 387 * reset the terminal modes, and exit. 388 */ 389 if (status < 0) 390 status = save_status; 391 else 392 save_status = status; 393 quitting = 1; 394 edit((char*)NULL); 395 save_cmdhist(); 396 if (any_display && is_tty) 397 clear_bot(); 398 deinit(); 399 flush(); 400 raw_mode(0); 401 #if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC 402 /* 403 * If we don't close 2, we get some garbage from 404 * 2's buffer when it flushes automatically. 405 * I cannot track this one down RB 406 * The same bug shows up if we use ^C^C to abort. 407 */ 408 close(2); 409 #endif 410 #if WIN32 411 SetConsoleTitle(consoleTitle); 412 #endif 413 close_getchr(); 414 exit(status); 415 } 416