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