1a5f0fb15SPaul Saab /* 2*c77c4889SXin LI * Copyright (C) 1984-2024 Mark Nudelman 3a5f0fb15SPaul Saab * 4a5f0fb15SPaul Saab * You may distribute under the terms of either the GNU General Public 5a5f0fb15SPaul Saab * License or the Less License, as specified in the README file. 6a5f0fb15SPaul Saab * 796e55cc7SXin LI * For more information, see the README file. 8a5f0fb15SPaul Saab */ 9a5f0fb15SPaul Saab 10a5f0fb15SPaul Saab 11a5f0fb15SPaul Saab #include "less.h" 12b2ea2440SXin LI #include "position.h" 13464501a8SXin LI #if HAVE_STAT 14464501a8SXin LI #include <sys/stat.h> 15464501a8SXin LI #endif 16d713e089SXin LI #if HAVE_SYS_WAIT_H 17d713e089SXin LI #include <sys/wait.h> 18b2ea2440SXin LI #endif 19*c77c4889SXin LI #if OS2 || __MVS__ || (defined WIFSIGNALED && defined WTERMSIG) 20d713e089SXin LI #include <signal.h> 21*c77c4889SXin LI #endif 22a5f0fb15SPaul Saab 23a5f0fb15SPaul Saab public int fd0 = 0; 24a5f0fb15SPaul Saab 25*c77c4889SXin LI extern lbool new_file; 26a5f0fb15SPaul Saab extern char *every_first_cmd; 27a5f0fb15SPaul Saab extern int force_open; 28a5f0fb15SPaul Saab extern int is_tty; 29a5f0fb15SPaul Saab extern int sigs; 302235c7feSXin LI extern int hshift; 3130a1828cSXin LI extern int want_filesize; 3295270f73SXin LI extern int consecutive_nulls; 33d713e089SXin LI extern int modelines; 34d713e089SXin LI extern int show_preproc_error; 35a5f0fb15SPaul Saab extern IFILE curr_ifile; 36a5f0fb15SPaul Saab extern IFILE old_ifile; 37a5f0fb15SPaul Saab extern struct scrpos initial_scrpos; 38f6b74a7dSXin LI extern void *ml_examine; 39a5f0fb15SPaul Saab #if SPACES_IN_FILENAMES 40a5f0fb15SPaul Saab extern char openquote; 41a5f0fb15SPaul Saab extern char closequote; 42a5f0fb15SPaul Saab #endif 43a5f0fb15SPaul Saab 44a5f0fb15SPaul Saab #if LOGFILE 45a5f0fb15SPaul Saab extern int logfile; 46a5f0fb15SPaul Saab extern int force_logfile; 47a5f0fb15SPaul Saab extern char *namelogfile; 48a5f0fb15SPaul Saab #endif 49a5f0fb15SPaul Saab 50464501a8SXin LI #if HAVE_STAT_INO 51464501a8SXin LI public dev_t curr_dev; 52464501a8SXin LI public ino_t curr_ino; 53464501a8SXin LI #endif 54464501a8SXin LI 55a5f0fb15SPaul Saab /* 56a5f0fb15SPaul Saab * Textlist functions deal with a list of words separated by spaces. 57a5f0fb15SPaul Saab * init_textlist sets up a textlist structure. 58a5f0fb15SPaul Saab * forw_textlist uses that structure to iterate thru the list of 59a5f0fb15SPaul Saab * words, returning each one as a standard null-terminated string. 60a5f0fb15SPaul Saab * back_textlist does the same, but runs thru the list backwards. 61a5f0fb15SPaul Saab */ 62*c77c4889SXin LI public void init_textlist(struct textlist *tlist, mutable char *str) 63a5f0fb15SPaul Saab { 64a5f0fb15SPaul Saab char *s; 65a5f0fb15SPaul Saab #if SPACES_IN_FILENAMES 66*c77c4889SXin LI lbool meta_quoted = FALSE; 67*c77c4889SXin LI lbool delim_quoted = FALSE; 68*c77c4889SXin LI constant char *esc = get_meta_escape(); 69*c77c4889SXin LI size_t esclen = strlen(esc); 70a5f0fb15SPaul Saab #endif 71a5f0fb15SPaul Saab 72a5f0fb15SPaul Saab tlist->string = skipsp(str); 73a5f0fb15SPaul Saab tlist->endstring = tlist->string + strlen(tlist->string); 74a5f0fb15SPaul Saab for (s = str; s < tlist->endstring; s++) 75a5f0fb15SPaul Saab { 76a5f0fb15SPaul Saab #if SPACES_IN_FILENAMES 77000ba3e8STim J. Robbins if (meta_quoted) 78000ba3e8STim J. Robbins { 79000ba3e8STim J. Robbins meta_quoted = 0; 80000ba3e8STim J. Robbins } else if (esclen > 0 && s + esclen < tlist->endstring && 81000ba3e8STim J. Robbins strncmp(s, esc, esclen) == 0) 82000ba3e8STim J. Robbins { 83000ba3e8STim J. Robbins meta_quoted = 1; 84000ba3e8STim J. Robbins s += esclen - 1; 85000ba3e8STim J. Robbins } else if (delim_quoted) 86000ba3e8STim J. Robbins { 87000ba3e8STim J. Robbins if (*s == closequote) 88000ba3e8STim J. Robbins delim_quoted = 0; 89000ba3e8STim J. Robbins } else /* (!delim_quoted) */ 90000ba3e8STim J. Robbins { 91000ba3e8STim J. Robbins if (*s == openquote) 92000ba3e8STim J. Robbins delim_quoted = 1; 93000ba3e8STim J. Robbins else if (*s == ' ') 94a5f0fb15SPaul Saab *s = '\0'; 95000ba3e8STim J. Robbins } 96a5f0fb15SPaul Saab #else 97a5f0fb15SPaul Saab if (*s == ' ') 98a5f0fb15SPaul Saab *s = '\0'; 99a5f0fb15SPaul Saab #endif 100a5f0fb15SPaul Saab } 101a5f0fb15SPaul Saab } 102a5f0fb15SPaul Saab 103*c77c4889SXin LI public constant char * forw_textlist(struct textlist *tlist, constant char *prev) 104a5f0fb15SPaul Saab { 105*c77c4889SXin LI constant char *s; 106a5f0fb15SPaul Saab 107a5f0fb15SPaul Saab /* 108a5f0fb15SPaul Saab * prev == NULL means return the first word in the list. 109a5f0fb15SPaul Saab * Otherwise, return the word after "prev". 110a5f0fb15SPaul Saab */ 111a5f0fb15SPaul Saab if (prev == NULL) 112a5f0fb15SPaul Saab s = tlist->string; 113a5f0fb15SPaul Saab else 114a5f0fb15SPaul Saab s = prev + strlen(prev); 115a5f0fb15SPaul Saab if (s >= tlist->endstring) 116a5f0fb15SPaul Saab return (NULL); 117a5f0fb15SPaul Saab while (*s == '\0') 118a5f0fb15SPaul Saab s++; 119a5f0fb15SPaul Saab if (s >= tlist->endstring) 120a5f0fb15SPaul Saab return (NULL); 121a5f0fb15SPaul Saab return (s); 122a5f0fb15SPaul Saab } 123a5f0fb15SPaul Saab 124*c77c4889SXin LI public constant char * back_textlist(struct textlist *tlist, constant char *prev) 125a5f0fb15SPaul Saab { 126*c77c4889SXin LI constant char *s; 127a5f0fb15SPaul Saab 128a5f0fb15SPaul Saab /* 129a5f0fb15SPaul Saab * prev == NULL means return the last word in the list. 130a5f0fb15SPaul Saab * Otherwise, return the word before "prev". 131a5f0fb15SPaul Saab */ 132a5f0fb15SPaul Saab if (prev == NULL) 133a5f0fb15SPaul Saab s = tlist->endstring; 134a5f0fb15SPaul Saab else if (prev <= tlist->string) 135a5f0fb15SPaul Saab return (NULL); 136a5f0fb15SPaul Saab else 137a5f0fb15SPaul Saab s = prev - 1; 138a5f0fb15SPaul Saab while (*s == '\0') 139a5f0fb15SPaul Saab s--; 140a5f0fb15SPaul Saab if (s <= tlist->string) 141a5f0fb15SPaul Saab return (NULL); 142a5f0fb15SPaul Saab while (s[-1] != '\0' && s > tlist->string) 143a5f0fb15SPaul Saab s--; 144a5f0fb15SPaul Saab return (s); 145a5f0fb15SPaul Saab } 146a5f0fb15SPaul Saab 147a5f0fb15SPaul Saab /* 148d713e089SXin LI * Parse a single option setting in a modeline. 149d713e089SXin LI */ 150*c77c4889SXin LI static void modeline_option(constant char *str, size_t opt_len) 151d713e089SXin LI { 152*c77c4889SXin LI struct mloption { constant char *opt_name; void (*opt_func)(constant char*,size_t); }; 153d713e089SXin LI struct mloption options[] = { 154d713e089SXin LI { "ts=", set_tabs }, 155d713e089SXin LI { "tabstop=", set_tabs }, 156d713e089SXin LI { NULL, NULL } 157d713e089SXin LI }; 158d713e089SXin LI struct mloption *opt; 159d713e089SXin LI for (opt = options; opt->opt_name != NULL; opt++) 160d713e089SXin LI { 161*c77c4889SXin LI size_t name_len = strlen(opt->opt_name); 162d713e089SXin LI if (opt_len > name_len && strncmp(str, opt->opt_name, name_len) == 0) 163d713e089SXin LI { 164d713e089SXin LI (*opt->opt_func)(str + name_len, opt_len - name_len); 165d713e089SXin LI break; 166d713e089SXin LI } 167d713e089SXin LI } 168d713e089SXin LI } 169d713e089SXin LI 170d713e089SXin LI /* 171d713e089SXin LI * String length, terminated by option separator (space or colon). 172d713e089SXin LI * Space/colon can be escaped with backspace. 173d713e089SXin LI */ 174*c77c4889SXin LI static size_t modeline_option_len(constant char *str) 175d713e089SXin LI { 176*c77c4889SXin LI lbool esc = FALSE; 177*c77c4889SXin LI constant char *s; 178d713e089SXin LI for (s = str; *s != '\0'; s++) 179d713e089SXin LI { 180d713e089SXin LI if (esc) 181d713e089SXin LI esc = FALSE; 182d713e089SXin LI else if (*s == '\\') 183d713e089SXin LI esc = TRUE; 184d713e089SXin LI else if (*s == ' ' || *s == ':') /* separator */ 185d713e089SXin LI break; 186d713e089SXin LI } 187*c77c4889SXin LI return ptr_diff(s, str); 188d713e089SXin LI } 189d713e089SXin LI 190d713e089SXin LI /* 191d713e089SXin LI * Parse colon- or space-separated option settings in a modeline. 192d713e089SXin LI */ 193*c77c4889SXin LI static void modeline_options(constant char *str, char end_char) 194d713e089SXin LI { 195d713e089SXin LI for (;;) 196d713e089SXin LI { 197*c77c4889SXin LI size_t opt_len; 198*c77c4889SXin LI str = skipspc(str); 199d713e089SXin LI if (*str == '\0' || *str == end_char) 200d713e089SXin LI break; 201d713e089SXin LI opt_len = modeline_option_len(str); 202d713e089SXin LI modeline_option(str, opt_len); 203d713e089SXin LI str += opt_len; 204d713e089SXin LI if (*str != '\0') 205d713e089SXin LI str += 1; /* skip past the separator */ 206d713e089SXin LI } 207d713e089SXin LI } 208d713e089SXin LI 209d713e089SXin LI /* 210d713e089SXin LI * See if there is a modeline string in a line. 211d713e089SXin LI */ 212*c77c4889SXin LI static void check_modeline(constant char *line) 213d713e089SXin LI { 214d713e089SXin LI #if HAVE_STRSTR 215*c77c4889SXin LI static constant char *pgms[] = { "less:", "vim:", "vi:", "ex:", NULL }; 216*c77c4889SXin LI constant char **pgm; 217d713e089SXin LI for (pgm = pgms; *pgm != NULL; ++pgm) 218d713e089SXin LI { 219*c77c4889SXin LI constant char *pline = line; 220d713e089SXin LI for (;;) 221d713e089SXin LI { 222*c77c4889SXin LI constant char *str; 223d713e089SXin LI pline = strstr(pline, *pgm); 224d713e089SXin LI if (pline == NULL) /* pgm is not in this line */ 225d713e089SXin LI break; 226*c77c4889SXin LI str = skipspc(pline + strlen(*pgm)); 227d713e089SXin LI if (pline == line || pline[-1] == ' ') 228d713e089SXin LI { 229d713e089SXin LI if (strncmp(str, "set ", 4) == 0) 230d713e089SXin LI modeline_options(str+4, ':'); 231d713e089SXin LI else if (pgm != &pgms[0]) /* "less:" requires "set" */ 232d713e089SXin LI modeline_options(str, '\0'); 233d713e089SXin LI break; 234d713e089SXin LI } 235d713e089SXin LI /* Continue searching the rest of the line. */ 236d713e089SXin LI pline = str; 237d713e089SXin LI } 238d713e089SXin LI } 239d713e089SXin LI #endif /* HAVE_STRSTR */ 240d713e089SXin LI } 241d713e089SXin LI 242d713e089SXin LI /* 243d713e089SXin LI * Read lines from start of file and check if any are modelines. 244d713e089SXin LI */ 245d713e089SXin LI static void check_modelines(void) 246d713e089SXin LI { 247d713e089SXin LI POSITION pos = ch_zero(); 248d713e089SXin LI int i; 249d713e089SXin LI for (i = 0; i < modelines; i++) 250d713e089SXin LI { 251*c77c4889SXin LI constant char *line; 252*c77c4889SXin LI size_t line_len; 253d713e089SXin LI if (ABORT_SIGS()) 254d713e089SXin LI return; 255d713e089SXin LI pos = forw_raw_line(pos, &line, &line_len); 256d713e089SXin LI if (pos == NULL_POSITION) 257d713e089SXin LI break; 258d713e089SXin LI check_modeline(line); 259d713e089SXin LI } 260d713e089SXin LI } 261d713e089SXin LI 262d713e089SXin LI /* 263b2ea2440SXin LI * Close a pipe opened via popen. 264b2ea2440SXin LI */ 265d713e089SXin LI static void close_pipe(FILE *pipefd) 266b2ea2440SXin LI { 267d713e089SXin LI int status; 268*c77c4889SXin LI char *p; 269d713e089SXin LI PARG parg; 270d713e089SXin LI 271b2ea2440SXin LI if (pipefd == NULL) 272b2ea2440SXin LI return; 273b2ea2440SXin LI #if OS2 274b2ea2440SXin LI /* 275b2ea2440SXin LI * The pclose function of OS/2 emx sometimes fails. 276b2ea2440SXin LI * Send SIGINT to the piped process before closing it. 277b2ea2440SXin LI */ 278b2ea2440SXin LI kill(pipefd->_pid, SIGINT); 279b2ea2440SXin LI #endif 280d713e089SXin LI status = pclose(pipefd); 281d713e089SXin LI if (status == -1) 282d713e089SXin LI { 283d713e089SXin LI /* An internal error in 'less', not a preprocessor error. */ 284*c77c4889SXin LI p = errno_message("pclose"); 285*c77c4889SXin LI parg.p_string = p; 286d713e089SXin LI error("%s", &parg); 287*c77c4889SXin LI free(p); 288d713e089SXin LI return; 289d713e089SXin LI } 290d713e089SXin LI if (!show_preproc_error) 291d713e089SXin LI return; 292d713e089SXin LI #if defined WIFEXITED && defined WEXITSTATUS 293d713e089SXin LI if (WIFEXITED(status)) 294d713e089SXin LI { 295d713e089SXin LI int s = WEXITSTATUS(status); 296d713e089SXin LI if (s != 0) 297d713e089SXin LI { 298d713e089SXin LI parg.p_int = s; 299d713e089SXin LI error("Input preprocessor failed (status %d)", &parg); 300d713e089SXin LI } 301d713e089SXin LI return; 302d713e089SXin LI } 303d713e089SXin LI #endif 304*c77c4889SXin LI #if defined WIFSIGNALED && defined WTERMSIG 305d713e089SXin LI if (WIFSIGNALED(status)) 306d713e089SXin LI { 307d713e089SXin LI int sig = WTERMSIG(status); 308d713e089SXin LI if (sig != SIGPIPE || ch_length() != NULL_POSITION) 309d713e089SXin LI { 310d713e089SXin LI parg.p_string = signal_message(sig); 311d713e089SXin LI error("Input preprocessor terminated: %s", &parg); 312d713e089SXin LI } 313d713e089SXin LI return; 314d713e089SXin LI } 315d713e089SXin LI #endif 316d713e089SXin LI if (status != 0) 317d713e089SXin LI { 318d713e089SXin LI parg.p_int = status; 319d713e089SXin LI error("Input preprocessor exited with status %x", &parg); 320d713e089SXin LI } 321d713e089SXin LI } 322d713e089SXin LI 323d713e089SXin LI /* 324d713e089SXin LI * Drain and close an input pipe if needed. 325d713e089SXin LI */ 326d713e089SXin LI public void close_altpipe(IFILE ifile) 327d713e089SXin LI { 328d713e089SXin LI FILE *altpipe = get_altpipe(ifile); 329d713e089SXin LI if (altpipe != NULL && !(ch_getflags() & CH_KEEPOPEN)) 330d713e089SXin LI { 331d713e089SXin LI close_pipe(altpipe); 332d713e089SXin LI set_altpipe(ifile, NULL); 333d713e089SXin LI } 334d713e089SXin LI } 335d713e089SXin LI 336d713e089SXin LI /* 337d713e089SXin LI * Check for error status from the current altpipe. 338d713e089SXin LI * May or may not close the pipe. 339d713e089SXin LI */ 340d713e089SXin LI public void check_altpipe_error(void) 341d713e089SXin LI { 342d713e089SXin LI if (!show_preproc_error) 343d713e089SXin LI return; 344d713e089SXin LI if (curr_ifile != NULL_IFILE && get_altfilename(curr_ifile) != NULL) 345d713e089SXin LI close_altpipe(curr_ifile); 346b2ea2440SXin LI } 347b2ea2440SXin LI 348b2ea2440SXin LI /* 349a5f0fb15SPaul Saab * Close the current input file. 350a5f0fb15SPaul Saab */ 351d713e089SXin LI static void close_file(void) 352a5f0fb15SPaul Saab { 353a5f0fb15SPaul Saab struct scrpos scrpos; 354*c77c4889SXin LI constant char *altfilename; 355a5f0fb15SPaul Saab 356a5f0fb15SPaul Saab if (curr_ifile == NULL_IFILE) 357a5f0fb15SPaul Saab return; 358a5f0fb15SPaul Saab 359a5f0fb15SPaul Saab /* 360a5f0fb15SPaul Saab * Save the current position so that we can return to 361a5f0fb15SPaul Saab * the same position if we edit this file again. 362a5f0fb15SPaul Saab */ 363b2ea2440SXin LI get_scrpos(&scrpos, TOP); 364a5f0fb15SPaul Saab if (scrpos.pos != NULL_POSITION) 365a5f0fb15SPaul Saab { 366a5f0fb15SPaul Saab store_pos(curr_ifile, &scrpos); 367a5f0fb15SPaul Saab lastmark(); 368a5f0fb15SPaul Saab } 369a5f0fb15SPaul Saab /* 370a5f0fb15SPaul Saab * Close the file descriptor, unless it is a pipe. 371a5f0fb15SPaul Saab */ 372a5f0fb15SPaul Saab ch_close(); 373a5f0fb15SPaul Saab /* 374a5f0fb15SPaul Saab * If we opened a file using an alternate name, 375a5f0fb15SPaul Saab * do special stuff to close it. 376a5f0fb15SPaul Saab */ 377b2ea2440SXin LI altfilename = get_altfilename(curr_ifile); 378b2ea2440SXin LI if (altfilename != NULL) 379a5f0fb15SPaul Saab { 380d713e089SXin LI close_altpipe(curr_ifile); 381b2ea2440SXin LI close_altfile(altfilename, get_filename(curr_ifile)); 382b2ea2440SXin LI set_altfilename(curr_ifile, NULL); 383a5f0fb15SPaul Saab } 384a5f0fb15SPaul Saab curr_ifile = NULL_IFILE; 385464501a8SXin LI #if HAVE_STAT_INO 386464501a8SXin LI curr_ino = curr_dev = 0; 387464501a8SXin LI #endif 388a5f0fb15SPaul Saab } 389a5f0fb15SPaul Saab 390a5f0fb15SPaul Saab /* 391a5f0fb15SPaul Saab * Edit a new file (given its name). 392a5f0fb15SPaul Saab * Filename == "-" means standard input. 393a5f0fb15SPaul Saab * Filename == NULL means just close the current file. 394a5f0fb15SPaul Saab */ 395*c77c4889SXin LI public int edit(constant char *filename) 396a5f0fb15SPaul Saab { 397a5f0fb15SPaul Saab if (filename == NULL) 398a5f0fb15SPaul Saab return (edit_ifile(NULL_IFILE)); 399a5f0fb15SPaul Saab return (edit_ifile(get_ifile(filename, curr_ifile))); 400a5f0fb15SPaul Saab } 401a5f0fb15SPaul Saab 402a5f0fb15SPaul Saab /* 403d713e089SXin LI * Clean up what edit_ifile did before error return. 404d713e089SXin LI */ 405*c77c4889SXin LI static int edit_error(constant char *filename, constant char *alt_filename, void *altpipe, IFILE ifile) 406d713e089SXin LI { 407d713e089SXin LI if (alt_filename != NULL) 408d713e089SXin LI { 409d713e089SXin LI close_pipe(altpipe); 410d713e089SXin LI close_altfile(alt_filename, filename); 411*c77c4889SXin LI free((char*)alt_filename); /* FIXME: WTF? */ 412d713e089SXin LI } 413d713e089SXin LI del_ifile(ifile); 414d713e089SXin LI /* 415d713e089SXin LI * Re-open the current file. 416d713e089SXin LI */ 417*c77c4889SXin LI if (curr_ifile == ifile) 418d713e089SXin LI { 419d713e089SXin LI /* 420d713e089SXin LI * Whoops. The "current" ifile is the one we just deleted. 421d713e089SXin LI * Just give up. 422d713e089SXin LI */ 423d713e089SXin LI quit(QUIT_ERROR); 424d713e089SXin LI } 425d713e089SXin LI return (1); 426d713e089SXin LI } 427d713e089SXin LI 428d713e089SXin LI /* 429a5f0fb15SPaul Saab * Edit a new file (given its IFILE). 430a5f0fb15SPaul Saab * ifile == NULL means just close the current file. 431a5f0fb15SPaul Saab */ 432d713e089SXin LI public int edit_ifile(IFILE ifile) 433a5f0fb15SPaul Saab { 434a5f0fb15SPaul Saab int f; 435a5f0fb15SPaul Saab int answer; 436a5f0fb15SPaul Saab int chflags; 437*c77c4889SXin LI constant char *filename; 438*c77c4889SXin LI constant char *open_filename; 439a5f0fb15SPaul Saab char *alt_filename; 440b2ea2440SXin LI void *altpipe; 441a5f0fb15SPaul Saab IFILE was_curr_ifile; 442*c77c4889SXin LI char *p; 443a5f0fb15SPaul Saab PARG parg; 444*c77c4889SXin LI ssize_t nread = 0; 445a5f0fb15SPaul Saab 446a5f0fb15SPaul Saab if (ifile == curr_ifile) 447a5f0fb15SPaul Saab { 448a5f0fb15SPaul Saab /* 449a5f0fb15SPaul Saab * Already have the correct file open. 450a5f0fb15SPaul Saab */ 451a5f0fb15SPaul Saab return (0); 452a5f0fb15SPaul Saab } 453*c77c4889SXin LI new_file = TRUE; 454a5f0fb15SPaul Saab 455*c77c4889SXin LI if (ifile != NULL_IFILE) 456a5f0fb15SPaul Saab { 457a5f0fb15SPaul Saab /* 458a5f0fb15SPaul Saab * See if LESSOPEN specifies an "alternate" file to open. 459a5f0fb15SPaul Saab */ 460*c77c4889SXin LI filename = get_filename(ifile); 461b2ea2440SXin LI altpipe = get_altpipe(ifile); 462b2ea2440SXin LI if (altpipe != NULL) 463b2ea2440SXin LI { 464b2ea2440SXin LI /* 465b2ea2440SXin LI * File is already open. 466b2ea2440SXin LI * chflags and f are not used by ch_init if ifile has 467b2ea2440SXin LI * filestate which should be the case if we're here. 468b2ea2440SXin LI * Set them here to avoid uninitialized variable warnings. 469b2ea2440SXin LI */ 470b2ea2440SXin LI chflags = 0; 471b2ea2440SXin LI f = -1; 472b2ea2440SXin LI alt_filename = get_altfilename(ifile); 473a5f0fb15SPaul Saab open_filename = (alt_filename != NULL) ? alt_filename : filename; 474b2ea2440SXin LI } else 475b2ea2440SXin LI { 476b2ea2440SXin LI if (strcmp(filename, FAKE_HELPFILE) == 0 || 477b2ea2440SXin LI strcmp(filename, FAKE_EMPTYFILE) == 0) 478b2ea2440SXin LI alt_filename = NULL; 479b2ea2440SXin LI else 480b2ea2440SXin LI alt_filename = open_altfile(filename, &f, &altpipe); 481b2ea2440SXin LI 482b2ea2440SXin LI open_filename = (alt_filename != NULL) ? alt_filename : filename; 483a5f0fb15SPaul Saab 484a5f0fb15SPaul Saab chflags = 0; 485b2ea2440SXin LI if (altpipe != NULL) 486a5f0fb15SPaul Saab { 487a5f0fb15SPaul Saab /* 488a5f0fb15SPaul Saab * The alternate "file" is actually a pipe. 489a5f0fb15SPaul Saab * f has already been set to the file descriptor of the pipe 490a5f0fb15SPaul Saab * in the call to open_altfile above. 491a5f0fb15SPaul Saab * Keep the file descriptor open because it was opened 492a5f0fb15SPaul Saab * via popen(), and pclose() wants to close it. 493a5f0fb15SPaul Saab */ 494a5f0fb15SPaul Saab chflags |= CH_POPENED; 495b2ea2440SXin LI if (strcmp(filename, "-") == 0) 496b2ea2440SXin LI chflags |= CH_KEEPOPEN; 497b2ea2440SXin LI } else if (strcmp(filename, "-") == 0) 498a5f0fb15SPaul Saab { 499a5f0fb15SPaul Saab /* 500a5f0fb15SPaul Saab * Use standard input. 501a5f0fb15SPaul Saab * Keep the file descriptor open because we can't reopen it. 502a5f0fb15SPaul Saab */ 503a5f0fb15SPaul Saab f = fd0; 504a5f0fb15SPaul Saab chflags |= CH_KEEPOPEN; 505a5f0fb15SPaul Saab /* 506a5f0fb15SPaul Saab * Must switch stdin to BINARY mode. 507a5f0fb15SPaul Saab */ 508a5f0fb15SPaul Saab SET_BINARY(f); 509a5f0fb15SPaul Saab #if MSDOS_COMPILER==DJGPPC 510a5f0fb15SPaul Saab /* 511a5f0fb15SPaul Saab * Setting stdin to binary by default causes 512a5f0fb15SPaul Saab * Ctrl-C to not raise SIGINT. We must undo 513a5f0fb15SPaul Saab * that side-effect. 514a5f0fb15SPaul Saab */ 515a5f0fb15SPaul Saab __djgpp_set_ctrl_c(1); 516a5f0fb15SPaul Saab #endif 51796e55cc7SXin LI } else if (strcmp(open_filename, FAKE_EMPTYFILE) == 0) 51896e55cc7SXin LI { 51996e55cc7SXin LI f = -1; 52096e55cc7SXin LI chflags |= CH_NODATA; 521a5f0fb15SPaul Saab } else if (strcmp(open_filename, FAKE_HELPFILE) == 0) 522a5f0fb15SPaul Saab { 523a5f0fb15SPaul Saab f = -1; 524a5f0fb15SPaul Saab chflags |= CH_HELPFILE; 525*c77c4889SXin LI } else if ((p = bad_file(open_filename)) != NULL) 526a5f0fb15SPaul Saab { 527a5f0fb15SPaul Saab /* 528a5f0fb15SPaul Saab * It looks like a bad file. Don't try to open it. 529a5f0fb15SPaul Saab */ 530*c77c4889SXin LI parg.p_string = p; 531a5f0fb15SPaul Saab error("%s", &parg); 532*c77c4889SXin LI free(p); 533*c77c4889SXin LI return edit_error(filename, alt_filename, altpipe, ifile); 534b2ea2440SXin LI } else if ((f = open(open_filename, OPEN_READ)) < 0) 535a5f0fb15SPaul Saab { 536a5f0fb15SPaul Saab /* 537a5f0fb15SPaul Saab * Got an error trying to open it. 538a5f0fb15SPaul Saab */ 539*c77c4889SXin LI char *p = errno_message(filename); 540*c77c4889SXin LI parg.p_string = p; 541a5f0fb15SPaul Saab error("%s", &parg); 542*c77c4889SXin LI free(p); 543*c77c4889SXin LI return edit_error(filename, alt_filename, altpipe, ifile); 544a5f0fb15SPaul Saab } else 545a5f0fb15SPaul Saab { 546a5f0fb15SPaul Saab chflags |= CH_CANSEEK; 547*c77c4889SXin LI if (bin_file(f, &nread) && !force_open && !opened(ifile)) 548a5f0fb15SPaul Saab { 549a5f0fb15SPaul Saab /* 550a5f0fb15SPaul Saab * Looks like a binary file. 551a5f0fb15SPaul Saab * Ask user if we should proceed. 552a5f0fb15SPaul Saab */ 553a5f0fb15SPaul Saab parg.p_string = filename; 554*c77c4889SXin LI answer = query("\"%s\" may be a binary file. See it anyway? ", &parg); 555a5f0fb15SPaul Saab if (answer != 'y' && answer != 'Y') 556a5f0fb15SPaul Saab { 557a5f0fb15SPaul Saab close(f); 558*c77c4889SXin LI return edit_error(filename, alt_filename, altpipe, ifile); 559a5f0fb15SPaul Saab } 560a5f0fb15SPaul Saab } 561a5f0fb15SPaul Saab } 562b2ea2440SXin LI } 563d713e089SXin LI if (!force_open && f >= 0 && isatty(f)) 564d713e089SXin LI { 565d713e089SXin LI PARG parg; 566d713e089SXin LI parg.p_string = filename; 567d713e089SXin LI error("%s is a terminal (use -f to open it)", &parg); 568*c77c4889SXin LI return edit_error(filename, alt_filename, altpipe, ifile); 569*c77c4889SXin LI } 570*c77c4889SXin LI } 571*c77c4889SXin LI 572*c77c4889SXin LI #if LOGFILE 573*c77c4889SXin LI end_logfile(); 574*c77c4889SXin LI #endif 575*c77c4889SXin LI was_curr_ifile = save_curr_ifile(); 576*c77c4889SXin LI if (curr_ifile != NULL_IFILE) 577*c77c4889SXin LI { 578*c77c4889SXin LI int was_helpfile = (ch_getflags() & CH_HELPFILE); 579*c77c4889SXin LI close_file(); 580*c77c4889SXin LI if (was_helpfile && held_ifile(was_curr_ifile) <= 1) 581*c77c4889SXin LI { 582*c77c4889SXin LI /* 583*c77c4889SXin LI * Don't keep the help file in the ifile list. 584*c77c4889SXin LI */ 585*c77c4889SXin LI del_ifile(was_curr_ifile); 586*c77c4889SXin LI was_curr_ifile = NULL_IFILE; 587*c77c4889SXin LI } 588*c77c4889SXin LI } 589*c77c4889SXin LI unsave_ifile(was_curr_ifile); 590*c77c4889SXin LI 591*c77c4889SXin LI if (ifile == NULL_IFILE) 592*c77c4889SXin LI { 593*c77c4889SXin LI /* 594*c77c4889SXin LI * No new file to open. 595*c77c4889SXin LI * (Don't set old_ifile, because if you call edit_ifile(NULL), 596*c77c4889SXin LI * you're supposed to have saved curr_ifile yourself, 597*c77c4889SXin LI * and you'll restore it if necessary.) 598*c77c4889SXin LI */ 599*c77c4889SXin LI return (0); 600d713e089SXin LI } 601a5f0fb15SPaul Saab 602a5f0fb15SPaul Saab /* 603*c77c4889SXin LI * Set up the new ifile. 604a5f0fb15SPaul Saab * Get the saved position for the file. 605a5f0fb15SPaul Saab */ 606a5f0fb15SPaul Saab curr_ifile = ifile; 607b2ea2440SXin LI set_altfilename(curr_ifile, alt_filename); 608b2ea2440SXin LI set_altpipe(curr_ifile, altpipe); 609a5f0fb15SPaul Saab set_open(curr_ifile); /* File has been opened */ 610a5f0fb15SPaul Saab get_pos(curr_ifile, &initial_scrpos); 611*c77c4889SXin LI ch_init(f, chflags, nread); 61295270f73SXin LI consecutive_nulls = 0; 613d713e089SXin LI check_modelines(); 614a5f0fb15SPaul Saab 615a5f0fb15SPaul Saab if (!(chflags & CH_HELPFILE)) 616a5f0fb15SPaul Saab { 617*c77c4889SXin LI if (was_curr_ifile != NULL_IFILE) 618*c77c4889SXin LI old_ifile = was_curr_ifile; 619a5f0fb15SPaul Saab #if LOGFILE 620a5f0fb15SPaul Saab if (namelogfile != NULL && is_tty) 621a5f0fb15SPaul Saab use_logfile(namelogfile); 622a5f0fb15SPaul Saab #endif 623464501a8SXin LI #if HAVE_STAT_INO 624464501a8SXin LI /* Remember the i-number and device of the opened file. */ 625b2ea2440SXin LI if (strcmp(open_filename, "-") != 0) 626464501a8SXin LI { 627464501a8SXin LI struct stat statbuf; 628b2ea2440SXin LI int r = stat(open_filename, &statbuf); 629464501a8SXin LI if (r == 0) 630464501a8SXin LI { 631464501a8SXin LI curr_ino = statbuf.st_ino; 632464501a8SXin LI curr_dev = statbuf.st_dev; 633464501a8SXin LI } 634464501a8SXin LI } 635464501a8SXin LI #endif 636a5f0fb15SPaul Saab if (every_first_cmd != NULL) 637a15691bfSXin LI { 638a5f0fb15SPaul Saab ungetsc(every_first_cmd); 639*c77c4889SXin LI ungetcc_end_command(); 640a5f0fb15SPaul Saab } 641a15691bfSXin LI } 642a5f0fb15SPaul Saab 643a5f0fb15SPaul Saab flush(); 644a5f0fb15SPaul Saab 645a5f0fb15SPaul Saab if (is_tty) 646a5f0fb15SPaul Saab { 647a5f0fb15SPaul Saab /* 648a5f0fb15SPaul Saab * Output is to a real tty. 649a5f0fb15SPaul Saab */ 650a5f0fb15SPaul Saab 651a5f0fb15SPaul Saab /* 652a5f0fb15SPaul Saab * Indicate there is nothing displayed yet. 653a5f0fb15SPaul Saab */ 654a5f0fb15SPaul Saab pos_clear(); 655a5f0fb15SPaul Saab clr_linenum(); 656a5f0fb15SPaul Saab #if HILITE_SEARCH 657a5f0fb15SPaul Saab clr_hilite(); 658a5f0fb15SPaul Saab #endif 6592235c7feSXin LI hshift = 0; 660a15691bfSXin LI if (strcmp(filename, FAKE_HELPFILE) && strcmp(filename, FAKE_EMPTYFILE)) 661b7780dbeSXin LI { 662b7780dbeSXin LI char *qfilename = shell_quote(filename); 663b7780dbeSXin LI cmd_addhist(ml_examine, qfilename, 1); 664b7780dbeSXin LI free(qfilename); 665b7780dbeSXin LI } 66630a1828cSXin LI if (want_filesize) 66730a1828cSXin LI scan_eof(); 668*c77c4889SXin LI set_header(ch_zero()); 669a5f0fb15SPaul Saab } 670a5f0fb15SPaul Saab return (0); 671a5f0fb15SPaul Saab } 672a5f0fb15SPaul Saab 673a5f0fb15SPaul Saab /* 674a5f0fb15SPaul Saab * Edit a space-separated list of files. 675a5f0fb15SPaul Saab * For each filename in the list, enter it into the ifile list. 676a5f0fb15SPaul Saab * Then edit the first one. 677a5f0fb15SPaul Saab */ 678d713e089SXin LI public int edit_list(char *filelist) 679a5f0fb15SPaul Saab { 680a5f0fb15SPaul Saab IFILE save_ifile; 681*c77c4889SXin LI constant char *good_filename; 682*c77c4889SXin LI constant char *filename; 683a5f0fb15SPaul Saab char *gfilelist; 684*c77c4889SXin LI constant char *gfilename; 685b2ea2440SXin LI char *qfilename; 686a5f0fb15SPaul Saab struct textlist tl_files; 687a5f0fb15SPaul Saab struct textlist tl_gfiles; 688a5f0fb15SPaul Saab 689a5f0fb15SPaul Saab save_ifile = save_curr_ifile(); 690a5f0fb15SPaul Saab good_filename = NULL; 691a5f0fb15SPaul Saab 692a5f0fb15SPaul Saab /* 693a5f0fb15SPaul Saab * Run thru each filename in the list. 694a5f0fb15SPaul Saab * Try to glob the filename. 695a5f0fb15SPaul Saab * If it doesn't expand, just try to open the filename. 696a5f0fb15SPaul Saab * If it does expand, try to open each name in that list. 697a5f0fb15SPaul Saab */ 698a5f0fb15SPaul Saab init_textlist(&tl_files, filelist); 699a5f0fb15SPaul Saab filename = NULL; 700a5f0fb15SPaul Saab while ((filename = forw_textlist(&tl_files, filename)) != NULL) 701a5f0fb15SPaul Saab { 702a5f0fb15SPaul Saab gfilelist = lglob(filename); 703a5f0fb15SPaul Saab init_textlist(&tl_gfiles, gfilelist); 704a5f0fb15SPaul Saab gfilename = NULL; 705a5f0fb15SPaul Saab while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL) 706a5f0fb15SPaul Saab { 707b2ea2440SXin LI qfilename = shell_unquote(gfilename); 708b2ea2440SXin LI if (edit(qfilename) == 0 && good_filename == NULL) 709a5f0fb15SPaul Saab good_filename = get_filename(curr_ifile); 710b2ea2440SXin LI free(qfilename); 711a5f0fb15SPaul Saab } 712a5f0fb15SPaul Saab free(gfilelist); 713a5f0fb15SPaul Saab } 714a5f0fb15SPaul Saab /* 715a5f0fb15SPaul Saab * Edit the first valid filename in the list. 716a5f0fb15SPaul Saab */ 717a5f0fb15SPaul Saab if (good_filename == NULL) 718a5f0fb15SPaul Saab { 719a5f0fb15SPaul Saab unsave_ifile(save_ifile); 720a5f0fb15SPaul Saab return (1); 721a5f0fb15SPaul Saab } 722a5f0fb15SPaul Saab if (get_ifile(good_filename, curr_ifile) == curr_ifile) 723a5f0fb15SPaul Saab { 724a5f0fb15SPaul Saab /* 725a5f0fb15SPaul Saab * Trying to edit the current file; don't reopen it. 726a5f0fb15SPaul Saab */ 727a5f0fb15SPaul Saab unsave_ifile(save_ifile); 728a5f0fb15SPaul Saab return (0); 729a5f0fb15SPaul Saab } 730a5f0fb15SPaul Saab reedit_ifile(save_ifile); 731a5f0fb15SPaul Saab return (edit(good_filename)); 732a5f0fb15SPaul Saab } 733a5f0fb15SPaul Saab 734a5f0fb15SPaul Saab /* 735a5f0fb15SPaul Saab * Edit the first file in the command line (ifile) list. 736a5f0fb15SPaul Saab */ 737d713e089SXin LI public int edit_first(void) 738a5f0fb15SPaul Saab { 739b7780dbeSXin LI if (nifile() == 0) 740b7780dbeSXin LI return (edit_stdin()); 741a5f0fb15SPaul Saab curr_ifile = NULL_IFILE; 742a5f0fb15SPaul Saab return (edit_next(1)); 743a5f0fb15SPaul Saab } 744a5f0fb15SPaul Saab 745a5f0fb15SPaul Saab /* 746a5f0fb15SPaul Saab * Edit the last file in the command line (ifile) list. 747a5f0fb15SPaul Saab */ 748d713e089SXin LI public int edit_last(void) 749a5f0fb15SPaul Saab { 750a5f0fb15SPaul Saab curr_ifile = NULL_IFILE; 751a5f0fb15SPaul Saab return (edit_prev(1)); 752a5f0fb15SPaul Saab } 753a5f0fb15SPaul Saab 754a5f0fb15SPaul Saab 755a5f0fb15SPaul Saab /* 7566dcb072bSXin LI * Edit the n-th next or previous file in the command line (ifile) list. 757a5f0fb15SPaul Saab */ 758d713e089SXin LI static int edit_istep(IFILE h, int n, int dir) 759a5f0fb15SPaul Saab { 760a5f0fb15SPaul Saab IFILE next; 761a5f0fb15SPaul Saab 762a5f0fb15SPaul Saab /* 763a5f0fb15SPaul Saab * Skip n filenames, then try to edit each filename. 764a5f0fb15SPaul Saab */ 765a5f0fb15SPaul Saab for (;;) 766a5f0fb15SPaul Saab { 767a5f0fb15SPaul Saab next = (dir > 0) ? next_ifile(h) : prev_ifile(h); 768a5f0fb15SPaul Saab if (--n < 0) 769a5f0fb15SPaul Saab { 770a5f0fb15SPaul Saab if (edit_ifile(h) == 0) 771a5f0fb15SPaul Saab break; 772a5f0fb15SPaul Saab } 773a5f0fb15SPaul Saab if (next == NULL_IFILE) 774a5f0fb15SPaul Saab { 775a5f0fb15SPaul Saab /* 776a5f0fb15SPaul Saab * Reached end of the ifile list. 777a5f0fb15SPaul Saab */ 778a5f0fb15SPaul Saab return (1); 779a5f0fb15SPaul Saab } 780a5f0fb15SPaul Saab if (ABORT_SIGS()) 781a5f0fb15SPaul Saab { 782a5f0fb15SPaul Saab /* 783a5f0fb15SPaul Saab * Interrupt breaks out, if we're in a long 784a5f0fb15SPaul Saab * list of files that can't be opened. 785a5f0fb15SPaul Saab */ 786a5f0fb15SPaul Saab return (1); 787a5f0fb15SPaul Saab } 788a5f0fb15SPaul Saab h = next; 789a5f0fb15SPaul Saab } 790a5f0fb15SPaul Saab /* 791a5f0fb15SPaul Saab * Found a file that we can edit. 792a5f0fb15SPaul Saab */ 793a5f0fb15SPaul Saab return (0); 794a5f0fb15SPaul Saab } 795a5f0fb15SPaul Saab 796d713e089SXin LI static int edit_inext(IFILE h, int n) 797a5f0fb15SPaul Saab { 7986dcb072bSXin LI return (edit_istep(h, n, +1)); 799a5f0fb15SPaul Saab } 800a5f0fb15SPaul Saab 801d713e089SXin LI public int edit_next(int n) 802a5f0fb15SPaul Saab { 8036dcb072bSXin LI return edit_istep(curr_ifile, n, +1); 804a5f0fb15SPaul Saab } 805a5f0fb15SPaul Saab 806d713e089SXin LI static int edit_iprev(IFILE h, int n) 807a5f0fb15SPaul Saab { 808a5f0fb15SPaul Saab return (edit_istep(h, n, -1)); 809a5f0fb15SPaul Saab } 810a5f0fb15SPaul Saab 811d713e089SXin LI public int edit_prev(int n) 812a5f0fb15SPaul Saab { 813a5f0fb15SPaul Saab return edit_istep(curr_ifile, n, -1); 814a5f0fb15SPaul Saab } 815a5f0fb15SPaul Saab 816a5f0fb15SPaul Saab /* 817a5f0fb15SPaul Saab * Edit a specific file in the command line (ifile) list. 818a5f0fb15SPaul Saab */ 819d713e089SXin LI public int edit_index(int n) 820a5f0fb15SPaul Saab { 821a5f0fb15SPaul Saab IFILE h; 822a5f0fb15SPaul Saab 823a5f0fb15SPaul Saab h = NULL_IFILE; 824a5f0fb15SPaul Saab do 825a5f0fb15SPaul Saab { 826a5f0fb15SPaul Saab if ((h = next_ifile(h)) == NULL_IFILE) 827a5f0fb15SPaul Saab { 828a5f0fb15SPaul Saab /* 829a5f0fb15SPaul Saab * Reached end of the list without finding it. 830a5f0fb15SPaul Saab */ 831a5f0fb15SPaul Saab return (1); 832a5f0fb15SPaul Saab } 833a5f0fb15SPaul Saab } while (get_index(h) != n); 834a5f0fb15SPaul Saab 835a5f0fb15SPaul Saab return (edit_ifile(h)); 836a5f0fb15SPaul Saab } 837a5f0fb15SPaul Saab 838d713e089SXin LI public IFILE save_curr_ifile(void) 839a5f0fb15SPaul Saab { 840a5f0fb15SPaul Saab if (curr_ifile != NULL_IFILE) 841a5f0fb15SPaul Saab hold_ifile(curr_ifile, 1); 842a5f0fb15SPaul Saab return (curr_ifile); 843a5f0fb15SPaul Saab } 844a5f0fb15SPaul Saab 845d713e089SXin LI public void unsave_ifile(IFILE save_ifile) 846a5f0fb15SPaul Saab { 847a5f0fb15SPaul Saab if (save_ifile != NULL_IFILE) 848a5f0fb15SPaul Saab hold_ifile(save_ifile, -1); 849a5f0fb15SPaul Saab } 850a5f0fb15SPaul Saab 851a5f0fb15SPaul Saab /* 852a5f0fb15SPaul Saab * Reedit the ifile which was previously open. 853a5f0fb15SPaul Saab */ 854d713e089SXin LI public void reedit_ifile(IFILE save_ifile) 855a5f0fb15SPaul Saab { 856a5f0fb15SPaul Saab IFILE next; 857a5f0fb15SPaul Saab IFILE prev; 858a5f0fb15SPaul Saab 859a5f0fb15SPaul Saab /* 860a5f0fb15SPaul Saab * Try to reopen the ifile. 861a5f0fb15SPaul Saab * Note that opening it may fail (maybe the file was removed), 862a5f0fb15SPaul Saab * in which case the ifile will be deleted from the list. 863a5f0fb15SPaul Saab * So save the next and prev ifiles first. 864a5f0fb15SPaul Saab */ 865a5f0fb15SPaul Saab unsave_ifile(save_ifile); 866a5f0fb15SPaul Saab next = next_ifile(save_ifile); 867a5f0fb15SPaul Saab prev = prev_ifile(save_ifile); 868a5f0fb15SPaul Saab if (edit_ifile(save_ifile) == 0) 869a5f0fb15SPaul Saab return; 870a5f0fb15SPaul Saab /* 871a5f0fb15SPaul Saab * If can't reopen it, open the next input file in the list. 872a5f0fb15SPaul Saab */ 873a5f0fb15SPaul Saab if (next != NULL_IFILE && edit_inext(next, 0) == 0) 874a5f0fb15SPaul Saab return; 875a5f0fb15SPaul Saab /* 876a5f0fb15SPaul Saab * If can't open THAT one, open the previous input file in the list. 877a5f0fb15SPaul Saab */ 878a5f0fb15SPaul Saab if (prev != NULL_IFILE && edit_iprev(prev, 0) == 0) 879a5f0fb15SPaul Saab return; 880a5f0fb15SPaul Saab /* 881a5f0fb15SPaul Saab * If can't even open that, we're stuck. Just quit. 882a5f0fb15SPaul Saab */ 883a5f0fb15SPaul Saab quit(QUIT_ERROR); 884a5f0fb15SPaul Saab } 885a5f0fb15SPaul Saab 886d713e089SXin LI public void reopen_curr_ifile(void) 887464501a8SXin LI { 888464501a8SXin LI IFILE save_ifile = save_curr_ifile(); 889464501a8SXin LI close_file(); 890464501a8SXin LI reedit_ifile(save_ifile); 891464501a8SXin LI } 892464501a8SXin LI 893a5f0fb15SPaul Saab /* 894a5f0fb15SPaul Saab * Edit standard input. 895a5f0fb15SPaul Saab */ 896d713e089SXin LI public int edit_stdin(void) 897a5f0fb15SPaul Saab { 898a5f0fb15SPaul Saab if (isatty(fd0)) 899a5f0fb15SPaul Saab { 900a5f0fb15SPaul Saab error("Missing filename (\"less --help\" for help)", NULL_PARG); 901a5f0fb15SPaul Saab quit(QUIT_OK); 902a5f0fb15SPaul Saab } 903a5f0fb15SPaul Saab return (edit("-")); 904a5f0fb15SPaul Saab } 905a5f0fb15SPaul Saab 906a5f0fb15SPaul Saab /* 907a5f0fb15SPaul Saab * Copy a file directly to standard output. 908a5f0fb15SPaul Saab * Used if standard output is not a tty. 909a5f0fb15SPaul Saab */ 910d713e089SXin LI public void cat_file(void) 911a5f0fb15SPaul Saab { 9121ea31627SRobert Watson int c; 913a5f0fb15SPaul Saab 914a5f0fb15SPaul Saab while ((c = ch_forw_get()) != EOI) 915a5f0fb15SPaul Saab putchr(c); 916a5f0fb15SPaul Saab flush(); 917a5f0fb15SPaul Saab } 918a5f0fb15SPaul Saab 919a5f0fb15SPaul Saab #if LOGFILE 920a5f0fb15SPaul Saab 9212235c7feSXin LI #define OVERWRITE_OPTIONS "Overwrite, Append, Don't log, or Quit?" 9222235c7feSXin LI 923a5f0fb15SPaul Saab /* 924a5f0fb15SPaul Saab * If the user asked for a log file and our input file 925a5f0fb15SPaul Saab * is standard input, create the log file. 926a5f0fb15SPaul Saab * We take care not to blindly overwrite an existing file. 927a5f0fb15SPaul Saab */ 928*c77c4889SXin LI public void use_logfile(constant char *filename) 929a5f0fb15SPaul Saab { 9301ea31627SRobert Watson int exists; 9311ea31627SRobert Watson int answer; 932a5f0fb15SPaul Saab PARG parg; 933a5f0fb15SPaul Saab 934a5f0fb15SPaul Saab if (ch_getflags() & CH_CANSEEK) 935a5f0fb15SPaul Saab /* 936a5f0fb15SPaul Saab * Can't currently use a log file on a file that can seek. 937a5f0fb15SPaul Saab */ 938a5f0fb15SPaul Saab return; 939a5f0fb15SPaul Saab 940a5f0fb15SPaul Saab /* 941a5f0fb15SPaul Saab * {{ We could use access() here. }} 942a5f0fb15SPaul Saab */ 943a5f0fb15SPaul Saab exists = open(filename, OPEN_READ); 944a15691bfSXin LI if (exists >= 0) 945a5f0fb15SPaul Saab close(exists); 946a5f0fb15SPaul Saab exists = (exists >= 0); 947a5f0fb15SPaul Saab 948a5f0fb15SPaul Saab /* 949a5f0fb15SPaul Saab * Decide whether to overwrite the log file or append to it. 950a5f0fb15SPaul Saab * If it doesn't exist we "overwrite" it. 951a5f0fb15SPaul Saab */ 952a5f0fb15SPaul Saab if (!exists || force_logfile) 953a5f0fb15SPaul Saab { 954a5f0fb15SPaul Saab /* 955a5f0fb15SPaul Saab * Overwrite (or create) the log file. 956a5f0fb15SPaul Saab */ 957a5f0fb15SPaul Saab answer = 'O'; 958a5f0fb15SPaul Saab } else 959a5f0fb15SPaul Saab { 960a5f0fb15SPaul Saab /* 961a5f0fb15SPaul Saab * Ask user what to do. 962a5f0fb15SPaul Saab */ 963a5f0fb15SPaul Saab parg.p_string = filename; 9642235c7feSXin LI answer = query("Warning: \"%s\" exists; "OVERWRITE_OPTIONS" ", &parg); 965a5f0fb15SPaul Saab } 966a5f0fb15SPaul Saab 967a5f0fb15SPaul Saab loop: 968a5f0fb15SPaul Saab switch (answer) 969a5f0fb15SPaul Saab { 970a5f0fb15SPaul Saab case 'O': case 'o': 971a5f0fb15SPaul Saab /* 972a5f0fb15SPaul Saab * Overwrite: create the file. 973a5f0fb15SPaul Saab */ 974f80a33eaSXin LI logfile = creat(filename, CREAT_RW); 975a5f0fb15SPaul Saab break; 976a5f0fb15SPaul Saab case 'A': case 'a': 977a5f0fb15SPaul Saab /* 978a5f0fb15SPaul Saab * Append: open the file and seek to the end. 979a5f0fb15SPaul Saab */ 980a5f0fb15SPaul Saab logfile = open(filename, OPEN_APPEND); 981*c77c4889SXin LI if (less_lseek(logfile, (less_off_t)0, SEEK_END) == BAD_LSEEK) 982a5f0fb15SPaul Saab { 983a5f0fb15SPaul Saab close(logfile); 984a5f0fb15SPaul Saab logfile = -1; 985a5f0fb15SPaul Saab } 986a5f0fb15SPaul Saab break; 987a5f0fb15SPaul Saab case 'D': case 'd': 988a5f0fb15SPaul Saab /* 989a5f0fb15SPaul Saab * Don't do anything. 990a5f0fb15SPaul Saab */ 991a5f0fb15SPaul Saab return; 992a5f0fb15SPaul Saab default: 993a5f0fb15SPaul Saab /* 994a5f0fb15SPaul Saab * Eh? 995a5f0fb15SPaul Saab */ 9962235c7feSXin LI 9972235c7feSXin LI answer = query(OVERWRITE_OPTIONS" (Type \"O\", \"A\", \"D\" or \"Q\") ", NULL_PARG); 998a5f0fb15SPaul Saab goto loop; 999a5f0fb15SPaul Saab } 1000a5f0fb15SPaul Saab 1001a5f0fb15SPaul Saab if (logfile < 0) 1002a5f0fb15SPaul Saab { 1003a5f0fb15SPaul Saab /* 1004a5f0fb15SPaul Saab * Error in opening logfile. 1005a5f0fb15SPaul Saab */ 1006a5f0fb15SPaul Saab parg.p_string = filename; 1007a5f0fb15SPaul Saab error("Cannot write to \"%s\"", &parg); 1008a5f0fb15SPaul Saab return; 1009a5f0fb15SPaul Saab } 1010a5f0fb15SPaul Saab SET_BINARY(logfile); 1011a5f0fb15SPaul Saab } 1012a5f0fb15SPaul Saab 1013a5f0fb15SPaul Saab #endif 1014