155714Skris/* $NetBSD: main.c,v 1.2 2011/07/03 19:51:26 tron Exp $ */ 255714Skris 355714Skris/* 455714Skris * Copyright (C) 1984-2011 Mark Nudelman 555714Skris * 655714Skris * You may distribute under the terms of either the GNU General Public 755714Skris * License or the Less License, as specified in the README file. 8280304Sjkim * 955714Skris * For more information about less, or for information on how to 1055714Skris * contact the author, see the README file. 1155714Skris */ 1255714Skris 1355714Skris 1455714Skris/* 15280304Sjkim * Entry point, initialization, miscellaneous routines. 1655714Skris */ 1755714Skris 1855714Skris#include "less.h" 1955714Skris#if MSDOS_COMPILER==WIN32C 2055714Skris#include <windows.h> 2155714Skris#endif 22280304Sjkim 2355714Skrispublic char * every_first_cmd = NULL; 2455714Skrispublic int new_file; 2555714Skrispublic int is_tty; 2655714Skrispublic IFILE curr_ifile = NULL_IFILE; 2755714Skrispublic IFILE old_ifile = NULL_IFILE; 2855714Skrispublic struct scrpos initial_scrpos; 2955714Skrispublic int any_display = FALSE; 3055714Skrispublic POSITION start_attnpos = NULL_POSITION; 3155714Skrispublic POSITION end_attnpos = NULL_POSITION; 3255714Skrispublic int wscroll; 3355714Skrispublic char * progname; 3455714Skrispublic int quitting; 3555714Skrispublic int secure; 3655714Skrispublic int dohelp; 37280304Sjkimpublic int more_mode = 0; 3855714Skris 3955714Skris#if LOGFILE 40280304Sjkimpublic int logfile = -1; 4155714Skrispublic int force_logfile = FALSE; 4255714Skrispublic char * namelogfile = NULL; 4355714Skris#endif 4455714Skris 4555714Skris#if EDITOR 4655714Skrispublic char * editor; 4755714Skrispublic char * editproto; 4855714Skris#endif 4955714Skris 5055714Skris#if TAGS 5155714Skrisextern char * tags; 52280304Sjkimextern char * tagoption; 5355714Skrisextern int jump_sline; 5455714Skris#endif 5555714Skris 5655714Skris#ifdef WIN32 5755714Skrisstatic char consoleTitle[256]; 5855714Skris#endif 5955714Skris 6055714Skrisextern int less_is_more; 6155714Skrisextern int missing_cap; 6255714Skrisextern int know_dumb; 6355714Skrisextern int quit_if_one_screen; 64280304Sjkimextern int pr_type; 65280304Sjkim 66280304Sjkim 67109998Smarkm/* 68280304Sjkim * Entry point. 69280304Sjkim */ 70280304Sjkimint 71280304Sjkimmain(argc, argv) 7255714Skris int argc; 7355714Skris char *argv[]; 74280304Sjkim{ 7555714Skris IFILE ifile; 76280304Sjkim char *s; 7755714Skris 78280304Sjkim#ifdef __EMX__ 79280304Sjkim _response(&argc, &argv); 80280304Sjkim _wildcard(&argc, &argv); 81280304Sjkim#endif 82280304Sjkim 83280304Sjkim progname = *argv++; 8455714Skris argc--; 85280304Sjkim 86280304Sjkim secure = 0; 87280304Sjkim s = lgetenv("LESSSECURE"); 88280304Sjkim if (s != NULL && *s != '\0') 89280304Sjkim secure = 1; 9059191Skris 91280304Sjkim#ifdef WIN32 92280304Sjkim if (getenv("HOME") == NULL) 93280304Sjkim { 9455714Skris /* 95280304Sjkim * If there is no HOME environment variable, 9655714Skris * try the concatenation of HOMEDRIVE + HOMEPATH. 97280304Sjkim */ 98280304Sjkim char *drive = getenv("HOMEDRIVE"); 9955714Skris char *path = getenv("HOMEPATH"); 100280304Sjkim if (drive != NULL && path != NULL) 10155714Skris { 102280304Sjkim char *env = (char *) ecalloc(strlen(drive) + 10355714Skris strlen(path) + 6, sizeof(char)); 104280304Sjkim strcpy(env, "HOME="); 105280304Sjkim strcat(env, drive); 10655714Skris strcat(env, path); 107280304Sjkim putenv(env); 10855714Skris } 109280304Sjkim } 11055714Skris GetConsoleTitle(consoleTitle, sizeof(consoleTitle)/sizeof(char)); 111280304Sjkim#endif /* WIN32 */ 112280304Sjkim 113280304Sjkim /* 114280304Sjkim * Process command line arguments and LESS environment arguments. 115280304Sjkim * Command line arguments override environment arguments. 11655714Skris */ 117280304Sjkim if (strcmp(getprogname(), "more") == 0) 118280304Sjkim more_mode = 1; 119280304Sjkim 120280304Sjkim is_tty = isatty(1); 121280304Sjkim get_term(); 122280304Sjkim init_cmds(); 123280304Sjkim init_charset(); 124280304Sjkim init_line(); 125280304Sjkim init_cmdhist(); 126280304Sjkim init_option(); 127280304Sjkim init_search(); 128280304Sjkim 129280304Sjkim /* 13055714Skris * If the name of the executable program is "more", 131280304Sjkim * act like LESS_IS_MORE is set. 132280304Sjkim */ 133280304Sjkim for (s = progname + strlen(progname); s > progname; s--) 13455714Skris { 135280304Sjkim if (s[-1] == PATHNAME_SEP[0]) 13655714Skris break; 137280304Sjkim } 138280304Sjkim if (strcmp(s, "more") == 0) 139280304Sjkim less_is_more = 1; 140280304Sjkim 141280304Sjkim init_prompt(); 142280304Sjkim 143280304Sjkim s = lgetenv(less_is_more ? "MORE" : "LESS"); 144280304Sjkim if (s != NULL) 145280304Sjkim scan_option(save(s)); 146280304Sjkim 147280304Sjkim#define isoptstring(s) (((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0') 148280304Sjkim while (argc > 0 && (isoptstring(*argv) || isoptpending())) 14959191Skris { 150280304Sjkim s = *argv++; 151280304Sjkim argc--; 152280304Sjkim if (strcmp(s, "--") == 0) 153280304Sjkim break; 154280304Sjkim scan_option(s); 15555714Skris } 15655714Skris#undef isoptstring 157109998Smarkm 158280304Sjkim if (isoptpending()) 159280304Sjkim { 160280304Sjkim /* 161280304Sjkim * Last command line option was a flag requiring a 16255714Skris * following string, but there was no following string. 163280304Sjkim */ 164280304Sjkim nopendopt(); 165280304Sjkim quit(QUIT_OK); 166280304Sjkim } 167280304Sjkim 168280304Sjkim if (less_is_more && get_quit_at_eof()) 16955714Skris quit_if_one_screen = TRUE; 170280304Sjkim 171280304Sjkim#if EDITOR 172280304Sjkim editor = lgetenv("VISUAL"); 173280304Sjkim if (editor == NULL || *editor == '\0') 174280304Sjkim { 17555714Skris editor = lgetenv("EDITOR"); 176280304Sjkim if (editor == NULL || *editor == '\0') 177280304Sjkim editor = EDIT_PGM; 178280304Sjkim } 179280304Sjkim editproto = lgetenv("LESSEDIT"); 180280304Sjkim if (editproto == NULL || *editproto == '\0') 181280304Sjkim editproto = "%E ?lm+%lm. %f"; 18255714Skris#endif 183280304Sjkim 18455714Skris /* 185280304Sjkim * Call get_ifile with all the command line filenames 18655714Skris * to "register" them with the ifile system. 187280304Sjkim */ 18855714Skris ifile = NULL_IFILE; 189280304Sjkim if (dohelp) 190280304Sjkim ifile = get_ifile(FAKE_HELPFILE, ifile); 19155714Skris while (argc-- > 0) 19255714Skris { 193280304Sjkim char *filename; 194280304Sjkim#if (MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC) 19568651Skris /* 19655714Skris * Because the "shell" doesn't expand filename patterns, 19755714Skris * treat each argument as a filename pattern rather than 19855714Skris * a single filename. 19959191Skris * Expand the pattern and iterate over the expanded list. 20055714Skris */ 20155714Skris struct textlist tlist; 20255714Skris char *gfilename; 20355714Skris 204109998Smarkm gfilename = lglob(*argv++); 205280304Sjkim init_textlist(&tlist, gfilename); 206280304Sjkim filename = NULL; 207280304Sjkim while ((filename = forw_textlist(&tlist, filename)) != NULL) 208280304Sjkim { 20955714Skris (void) get_ifile(filename, ifile); 210280304Sjkim ifile = prev_ifile(NULL_IFILE); 211280304Sjkim } 212280304Sjkim free(gfilename); 213280304Sjkim#else 214280304Sjkim filename = shell_quote(*argv); 215280304Sjkim if (filename == NULL) 216280304Sjkim filename = *argv; 217280304Sjkim argv++; 218280304Sjkim (void) get_ifile(filename, ifile); 219280304Sjkim ifile = prev_ifile(NULL_IFILE); 220280304Sjkim#endif 221280304Sjkim } 222280304Sjkim /* 223280304Sjkim * Set up terminal, etc. 224280304Sjkim */ 225280304Sjkim if (!is_tty) 226280304Sjkim { 227280304Sjkim /* 228280304Sjkim * Output is not a tty. 229280304Sjkim * Just copy the input file(s) to output. 230280304Sjkim */ 231280304Sjkim SET_BINARY(1); 232280304Sjkim if (nifile() == 0) 233280304Sjkim { 234280304Sjkim if (edit_stdin() == 0) 235280304Sjkim cat_file(); 236280304Sjkim } else if (edit_first() == 0) 237280304Sjkim { 238280304Sjkim do { 239280304Sjkim cat_file(); 240280304Sjkim } while (edit_next(1) == 0); 24155714Skris } 242280304Sjkim quit(QUIT_OK); 243280304Sjkim } 24455714Skris 245280304Sjkim if (missing_cap && !know_dumb && !more_mode) 246280304Sjkim error("WARNING: terminal is not fully functional", NULL_PARG); 247280304Sjkim init_mark(); 248280304Sjkim open_getchr(); 249280304Sjkim raw_mode(1); 250280304Sjkim init_signals(1); 25155714Skris 252280304Sjkim /* 253280304Sjkim * Select the first file to examine. 254280304Sjkim */ 255280304Sjkim#if TAGS 256280304Sjkim if (tagoption != NULL || strcmp(tags, "-") == 0) 25755714Skris { 258280304Sjkim /* 25955714Skris * A -t option was given. 260280304Sjkim * Verify that no filenames were also given. 261280304Sjkim * Edit the file selected by the "tags" search, 26255714Skris * and search for the proper line in the file. 263280304Sjkim */ 264280304Sjkim if (nifile() > 0) 265280304Sjkim { 266280304Sjkim error("No filenames allowed with -t option", NULL_PARG); 267280304Sjkim quit(QUIT_ERROR); 268280304Sjkim } 269280304Sjkim findtag(tagoption); 270280304Sjkim if (edit_tagfile()) /* Edit file which contains the tag */ 271280304Sjkim quit(QUIT_ERROR); 272280304Sjkim /* 273280304Sjkim * Search for the line which contains the tag. 274280304Sjkim * Set up initial_scrpos so we display that line. 27555714Skris */ 276280304Sjkim initial_scrpos.pos = tagsearch(); 277280304Sjkim if (initial_scrpos.pos == NULL_POSITION) 278280304Sjkim quit(QUIT_ERROR); 279280304Sjkim initial_scrpos.ln = jump_sline; 280280304Sjkim } else 281280304Sjkim#endif 282280304Sjkim if (nifile() == 0) 283280304Sjkim { 284280304Sjkim if (edit_stdin()) /* Edit standard input */ 285280304Sjkim quit(QUIT_ERROR); 286280304Sjkim } else 287280304Sjkim { 288280304Sjkim if (edit_first()) /* Edit first valid file in cmd line */ 289280304Sjkim quit(QUIT_ERROR); 29055714Skris } 291 292 init(); 293 commands(); 294 quit(QUIT_OK); 295 /*NOTREACHED*/ 296 return (0); 297} 298 299/* 300 * Copy a string to a "safe" place 301 * (that is, to a buffer allocated by calloc). 302 */ 303 public char * 304save(s) 305 char *s; 306{ 307 register char *p; 308 309 p = (char *) ecalloc(strlen(s)+1, sizeof(char)); 310 strcpy(p, s); 311 return (p); 312} 313 314/* 315 * Allocate memory. 316 * Like calloc(), but never returns an error (NULL). 317 */ 318 public VOID_POINTER 319ecalloc(count, size) 320 int count; 321 unsigned int size; 322{ 323 register VOID_POINTER p; 324 325 p = (VOID_POINTER) calloc(count, size); 326 if (p != NULL) 327 return (p); 328 error("Cannot allocate memory", NULL_PARG); 329 quit(QUIT_ERROR); 330 /*NOTREACHED*/ 331 return (NULL); 332} 333 334/* 335 * Skip leading spaces in a string. 336 */ 337 public char * 338skipsp(s) 339 register char *s; 340{ 341 while (*s == ' ' || *s == '\t') 342 s++; 343 return (s); 344} 345 346/* 347 * See how many characters of two strings are identical. 348 * If uppercase is true, the first string must begin with an uppercase 349 * character; the remainder of the first string may be either case. 350 */ 351 public int 352sprefix(ps, s, uppercase) 353 char *ps; 354 char *s; 355 int uppercase; 356{ 357 register int c; 358 register int sc; 359 register int len = 0; 360 361 for ( ; *s != '\0'; s++, ps++) 362 { 363 c = *ps; 364 if (uppercase) 365 { 366 if (len == 0 && ASCII_IS_LOWER(c)) 367 return (-1); 368 if (ASCII_IS_UPPER(c)) 369 c = ASCII_TO_LOWER(c); 370 } 371 sc = *s; 372 if (len > 0 && ASCII_IS_UPPER(sc)) 373 sc = ASCII_TO_LOWER(sc); 374 if (c != sc) 375 break; 376 len++; 377 } 378 return (len); 379} 380 381/* 382 * Exit the program. 383 */ 384 public void 385quit(status) 386 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 edit((char*)NULL); 400 save_cmdhist(); 401 if (any_display && is_tty) 402 clear_bot(); 403 deinit(); 404 flush(); 405 raw_mode(0); 406#if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC 407 /* 408 * If we don't close 2, we get some garbage from 409 * 2's buffer when it flushes automatically. 410 * I cannot track this one down RB 411 * The same bug shows up if we use ^C^C to abort. 412 */ 413 close(2); 414#endif 415#ifdef WIN32 416 SetConsoleTitle(consoleTitle); 417#endif 418 close_getchr(); 419 exit(status); 420} 421