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