1 |
|
|
/* |
2 |
|
|
* Copyright (C) 1984-2012 Mark Nudelman |
3 |
|
|
* Modified for use with illumos by Garrett D'Amore. |
4 |
|
|
* Copyright 2014 Garrett D'Amore <garrett@damore.org> |
5 |
|
|
* |
6 |
|
|
* You may distribute under the terms of either the GNU General Public |
7 |
|
|
* License or the Less License, as specified in the README file. |
8 |
|
|
* |
9 |
|
|
* For more information, see the README file. |
10 |
|
|
*/ |
11 |
|
|
|
12 |
|
|
#include <sys/stat.h> |
13 |
|
|
|
14 |
|
|
#include "less.h" |
15 |
|
|
|
16 |
|
|
static int fd0 = 0; |
17 |
|
|
|
18 |
|
|
extern int new_file; |
19 |
|
|
extern int errmsgs; |
20 |
|
|
extern char *every_first_cmd; |
21 |
|
|
extern int any_display; |
22 |
|
|
extern int force_open; |
23 |
|
|
extern int is_tty; |
24 |
|
|
extern volatile sig_atomic_t sigs; |
25 |
|
|
extern IFILE curr_ifile; |
26 |
|
|
extern IFILE old_ifile; |
27 |
|
|
extern struct scrpos initial_scrpos; |
28 |
|
|
extern void *ml_examine; |
29 |
|
|
extern char openquote; |
30 |
|
|
extern char closequote; |
31 |
|
|
extern int less_is_more; |
32 |
|
|
extern int logfile; |
33 |
|
|
extern int force_logfile; |
34 |
|
|
extern char *namelogfile; |
35 |
|
|
|
36 |
|
|
dev_t curr_dev; |
37 |
|
|
ino_t curr_ino; |
38 |
|
|
|
39 |
|
|
char *curr_altfilename = NULL; |
40 |
|
|
static void *curr_altpipe; |
41 |
|
|
|
42 |
|
|
|
43 |
|
|
/* |
44 |
|
|
* Textlist functions deal with a list of words separated by spaces. |
45 |
|
|
* init_textlist sets up a textlist structure. |
46 |
|
|
* forw_textlist uses that structure to iterate thru the list of |
47 |
|
|
* words, returning each one as a standard null-terminated string. |
48 |
|
|
* back_textlist does the same, but runs thru the list backwards. |
49 |
|
|
*/ |
50 |
|
|
void |
51 |
|
|
init_textlist(struct textlist *tlist, char *str) |
52 |
|
|
{ |
53 |
|
|
char *s; |
54 |
|
|
int meta_quoted = 0; |
55 |
|
|
int delim_quoted = 0; |
56 |
|
|
char *esc = get_meta_escape(); |
57 |
|
|
int esclen = strlen(esc); |
58 |
|
|
|
59 |
|
|
tlist->string = skipsp(str); |
60 |
|
|
tlist->endstring = tlist->string + strlen(tlist->string); |
61 |
|
|
for (s = str; s < tlist->endstring; s++) { |
62 |
|
|
if (meta_quoted) { |
63 |
|
|
meta_quoted = 0; |
64 |
|
|
} else if (esclen > 0 && s + esclen < tlist->endstring && |
65 |
|
|
strncmp(s, esc, esclen) == 0) { |
66 |
|
|
meta_quoted = 1; |
67 |
|
|
s += esclen - 1; |
68 |
|
|
} else if (delim_quoted) { |
69 |
|
|
if (*s == closequote) |
70 |
|
|
delim_quoted = 0; |
71 |
|
|
} else /* (!delim_quoted) */ { |
72 |
|
|
if (*s == openquote) |
73 |
|
|
delim_quoted = 1; |
74 |
|
|
else if (*s == ' ') |
75 |
|
|
*s = '\0'; |
76 |
|
|
} |
77 |
|
|
} |
78 |
|
|
} |
79 |
|
|
|
80 |
|
|
char * |
81 |
|
|
forw_textlist(struct textlist *tlist, char *prev) |
82 |
|
|
{ |
83 |
|
|
char *s; |
84 |
|
|
|
85 |
|
|
/* |
86 |
|
|
* prev == NULL means return the first word in the list. |
87 |
|
|
* Otherwise, return the word after "prev". |
88 |
|
|
*/ |
89 |
|
|
if (prev == NULL) |
90 |
|
|
s = tlist->string; |
91 |
|
|
else |
92 |
|
|
s = prev + strlen(prev); |
93 |
|
|
if (s >= tlist->endstring) |
94 |
|
|
return (NULL); |
95 |
|
|
while (*s == '\0') |
96 |
|
|
s++; |
97 |
|
|
if (s >= tlist->endstring) |
98 |
|
|
return (NULL); |
99 |
|
|
return (s); |
100 |
|
|
} |
101 |
|
|
|
102 |
|
|
char * |
103 |
|
|
back_textlist(struct textlist *tlist, char *prev) |
104 |
|
|
{ |
105 |
|
|
char *s; |
106 |
|
|
|
107 |
|
|
/* |
108 |
|
|
* prev == NULL means return the last word in the list. |
109 |
|
|
* Otherwise, return the word before "prev". |
110 |
|
|
*/ |
111 |
|
|
if (prev == NULL) |
112 |
|
|
s = tlist->endstring; |
113 |
|
|
else if (prev <= tlist->string) |
114 |
|
|
return (NULL); |
115 |
|
|
else |
116 |
|
|
s = prev - 1; |
117 |
|
|
while (*s == '\0') |
118 |
|
|
s--; |
119 |
|
|
if (s <= tlist->string) |
120 |
|
|
return (NULL); |
121 |
|
|
while (s[-1] != '\0' && s > tlist->string) |
122 |
|
|
s--; |
123 |
|
|
return (s); |
124 |
|
|
} |
125 |
|
|
|
126 |
|
|
/* |
127 |
|
|
* Close the current input file. |
128 |
|
|
*/ |
129 |
|
|
static void |
130 |
|
|
close_file(void) |
131 |
|
|
{ |
132 |
|
74 |
struct scrpos scrpos; |
133 |
|
|
|
134 |
✗✓ |
37 |
if (curr_ifile == NULL) |
135 |
|
|
return; |
136 |
|
|
|
137 |
|
|
/* |
138 |
|
|
* Save the current position so that we can return to |
139 |
|
|
* the same position if we edit this file again. |
140 |
|
|
*/ |
141 |
|
37 |
get_scrpos(&scrpos); |
142 |
✓✗ |
37 |
if (scrpos.pos != -1) { |
143 |
|
37 |
store_pos(curr_ifile, &scrpos); |
144 |
|
37 |
lastmark(); |
145 |
|
37 |
} |
146 |
|
|
/* |
147 |
|
|
* Close the file descriptor, unless it is a pipe. |
148 |
|
|
*/ |
149 |
|
37 |
ch_close(); |
150 |
|
|
/* |
151 |
|
|
* If we opened a file using an alternate name, |
152 |
|
|
* do special stuff to close it. |
153 |
|
|
*/ |
154 |
✗✓ |
37 |
if (curr_altfilename != NULL) { |
155 |
|
|
close_altfile(curr_altfilename, get_filename(curr_ifile), |
156 |
|
|
curr_altpipe); |
157 |
|
|
free(curr_altfilename); |
158 |
|
|
curr_altfilename = NULL; |
159 |
|
|
} |
160 |
|
37 |
curr_ifile = NULL; |
161 |
|
37 |
curr_ino = curr_dev = 0; |
162 |
|
74 |
} |
163 |
|
|
|
164 |
|
|
/* |
165 |
|
|
* Edit a new file (given its name). |
166 |
|
|
* Filename == "-" means standard input. |
167 |
|
|
* Filename == NULL means just close the current file. |
168 |
|
|
*/ |
169 |
|
|
int |
170 |
|
|
edit(char *filename) |
171 |
|
|
{ |
172 |
✓✓ |
102 |
if (filename == NULL) |
173 |
|
39 |
return (edit_ifile(NULL)); |
174 |
|
12 |
return (edit_ifile(get_ifile(filename, curr_ifile))); |
175 |
|
51 |
} |
176 |
|
|
|
177 |
|
|
/* |
178 |
|
|
* Edit a new file (given its IFILE). |
179 |
|
|
* ifile == NULL means just close the current file. |
180 |
|
|
*/ |
181 |
|
|
int |
182 |
|
|
edit_ifile(IFILE ifile) |
183 |
|
|
{ |
184 |
|
160 |
int f; |
185 |
|
|
int answer; |
186 |
|
|
int no_display; |
187 |
|
|
int chflags; |
188 |
|
|
char *filename; |
189 |
|
|
char *open_filename; |
190 |
|
|
char *qopen_filename; |
191 |
|
|
char *alt_filename; |
192 |
|
80 |
void *alt_pipe; |
193 |
|
|
IFILE was_curr_ifile; |
194 |
|
80 |
PARG parg; |
195 |
|
|
|
196 |
✓✓ |
80 |
if (ifile == curr_ifile) { |
197 |
|
|
/* |
198 |
|
|
* Already have the correct file open. |
199 |
|
|
*/ |
200 |
|
4 |
return (0); |
201 |
|
|
} |
202 |
|
|
|
203 |
|
|
/* |
204 |
|
|
* We must close the currently open file now. |
205 |
|
|
* This is necessary to make the open_altfile/close_altfile pairs |
206 |
|
|
* nest properly (or rather to avoid nesting at all). |
207 |
|
|
* {{ Some stupid implementations of popen() mess up if you do: |
208 |
|
|
* fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }} |
209 |
|
|
*/ |
210 |
|
76 |
end_logfile(); |
211 |
|
76 |
was_curr_ifile = save_curr_ifile(); |
212 |
✓✓ |
76 |
if (curr_ifile != NULL) { |
213 |
|
37 |
chflags = ch_getflags(); |
214 |
|
37 |
close_file(); |
215 |
✗✓✗✗
|
37 |
if ((chflags & CH_HELPFILE) && |
216 |
|
|
held_ifile(was_curr_ifile) <= 1) { |
217 |
|
|
/* |
218 |
|
|
* Don't keep the help file in the ifile list. |
219 |
|
|
*/ |
220 |
|
|
del_ifile(was_curr_ifile); |
221 |
|
|
was_curr_ifile = old_ifile; |
222 |
|
|
} |
223 |
|
|
} |
224 |
|
|
|
225 |
✓✓ |
76 |
if (ifile == NULL) { |
226 |
|
|
/* |
227 |
|
|
* No new file to open. |
228 |
|
|
* (Don't set old_ifile, because if you call edit_ifile(NULL), |
229 |
|
|
* you're supposed to have saved curr_ifile yourself, |
230 |
|
|
* and you'll restore it if necessary.) |
231 |
|
|
*/ |
232 |
|
37 |
unsave_ifile(was_curr_ifile); |
233 |
|
37 |
return (0); |
234 |
|
|
} |
235 |
|
|
|
236 |
|
39 |
filename = estrdup(get_filename(ifile)); |
237 |
|
|
/* |
238 |
|
|
* See if LESSOPEN specifies an "alternate" file to open. |
239 |
|
|
*/ |
240 |
|
39 |
alt_pipe = NULL; |
241 |
|
39 |
alt_filename = open_altfile(filename, &f, &alt_pipe); |
242 |
|
39 |
open_filename = (alt_filename != NULL) ? alt_filename : filename; |
243 |
|
39 |
qopen_filename = shell_unquote(open_filename); |
244 |
|
|
|
245 |
|
|
chflags = 0; |
246 |
✗✓ |
39 |
if (strcmp(open_filename, helpfile()) == 0) |
247 |
|
|
chflags |= CH_HELPFILE; |
248 |
✗✓ |
39 |
if (alt_pipe != NULL) { |
249 |
|
|
/* |
250 |
|
|
* The alternate "file" is actually a pipe. |
251 |
|
|
* f has already been set to the file descriptor of the pipe |
252 |
|
|
* in the call to open_altfile above. |
253 |
|
|
* Keep the file descriptor open because it was opened |
254 |
|
|
* via popen(), and pclose() wants to close it. |
255 |
|
|
*/ |
256 |
|
|
chflags |= CH_POPENED; |
257 |
✓✓ |
39 |
} else if (strcmp(open_filename, "-") == 0) { |
258 |
|
|
/* |
259 |
|
|
* Use standard input. |
260 |
|
|
* Keep the file descriptor open because we can't reopen it. |
261 |
|
|
*/ |
262 |
|
12 |
f = fd0; |
263 |
|
12 |
chflags |= CH_KEEPOPEN; |
264 |
✗✓ |
39 |
} else if (strcmp(open_filename, FAKE_EMPTYFILE) == 0) { |
265 |
|
|
f = -1; |
266 |
|
|
chflags |= CH_NODATA; |
267 |
✓✓ |
27 |
} else if ((parg.p_string = bad_file(open_filename)) != NULL) { |
268 |
|
|
/* |
269 |
|
|
* It looks like a bad file. Don't try to open it. |
270 |
|
|
*/ |
271 |
|
2 |
error("%s", &parg); |
272 |
|
2 |
free(parg.p_string); |
273 |
|
|
err1: |
274 |
✗✓ |
2 |
if (alt_filename != NULL) { |
275 |
|
|
close_altfile(alt_filename, filename, alt_pipe); |
276 |
|
|
free(alt_filename); |
277 |
|
|
} |
278 |
|
2 |
del_ifile(ifile); |
279 |
|
2 |
free(qopen_filename); |
280 |
|
2 |
free(filename); |
281 |
|
|
/* |
282 |
|
|
* Re-open the current file. |
283 |
|
|
*/ |
284 |
✗✓ |
2 |
if (was_curr_ifile == ifile) { |
285 |
|
|
/* |
286 |
|
|
* Whoops. The "current" ifile is the one we just |
287 |
|
|
* deleted. Just give up. |
288 |
|
|
*/ |
289 |
|
|
quit(QUIT_ERROR); |
290 |
|
|
} |
291 |
|
2 |
reedit_ifile(was_curr_ifile); |
292 |
|
2 |
return (1); |
293 |
✗✓ |
25 |
} else if ((f = open(qopen_filename, O_RDONLY)) < 0) { |
294 |
|
|
/* |
295 |
|
|
* Got an error trying to open it. |
296 |
|
|
*/ |
297 |
|
|
parg.p_string = errno_message(filename); |
298 |
|
|
error("%s", &parg); |
299 |
|
|
free(parg.p_string); |
300 |
|
|
goto err1; |
301 |
|
|
} else { |
302 |
|
25 |
chflags |= CH_CANSEEK; |
303 |
✓✗✓✗ ✗✓ |
75 |
if (!force_open && !opened(ifile) && bin_file(f)) { |
304 |
|
|
/* |
305 |
|
|
* Looks like a binary file. |
306 |
|
|
* Ask user if we should proceed. |
307 |
|
|
*/ |
308 |
|
|
parg.p_string = filename; |
309 |
|
|
answer = query("\"%s\" may be a binary file. " |
310 |
|
|
"See it anyway? ", &parg); |
311 |
|
|
if (answer != 'y' && answer != 'Y') { |
312 |
|
|
(void) close(f); |
313 |
|
|
goto err1; |
314 |
|
|
} |
315 |
|
|
} |
316 |
|
|
} |
317 |
|
|
|
318 |
|
|
/* |
319 |
|
|
* Get the new ifile. |
320 |
|
|
* Get the saved position for the file. |
321 |
|
|
*/ |
322 |
✗✓ |
37 |
if (was_curr_ifile != NULL) { |
323 |
|
|
old_ifile = was_curr_ifile; |
324 |
|
|
unsave_ifile(was_curr_ifile); |
325 |
|
|
} |
326 |
|
37 |
curr_ifile = ifile; |
327 |
|
37 |
curr_altfilename = alt_filename; |
328 |
|
37 |
curr_altpipe = alt_pipe; |
329 |
|
37 |
set_open(curr_ifile); /* File has been opened */ |
330 |
|
37 |
get_pos(curr_ifile, &initial_scrpos); |
331 |
|
37 |
new_file = TRUE; |
332 |
|
37 |
ch_init(f, chflags); |
333 |
|
|
|
334 |
✓✗ |
37 |
if (!(chflags & CH_HELPFILE)) { |
335 |
|
37 |
struct stat statbuf; |
336 |
|
|
int r; |
337 |
|
|
|
338 |
✗✓ |
37 |
if (namelogfile != NULL && is_tty) |
339 |
|
|
use_logfile(namelogfile); |
340 |
|
|
/* Remember the i-number and device of opened file. */ |
341 |
|
37 |
r = stat(qopen_filename, &statbuf); |
342 |
✓✓ |
37 |
if (r == 0) { |
343 |
|
25 |
curr_ino = statbuf.st_ino; |
344 |
|
25 |
curr_dev = statbuf.st_dev; |
345 |
|
25 |
} |
346 |
✗✓ |
37 |
if (every_first_cmd != NULL) |
347 |
|
|
ungetsc(every_first_cmd); |
348 |
|
37 |
} |
349 |
|
37 |
free(qopen_filename); |
350 |
|
37 |
no_display = !any_display; |
351 |
|
37 |
flush(0); |
352 |
|
37 |
any_display = TRUE; |
353 |
|
|
|
354 |
✓✗ |
37 |
if (is_tty) { |
355 |
|
|
/* |
356 |
|
|
* Output is to a real tty. |
357 |
|
|
*/ |
358 |
|
|
|
359 |
|
|
/* |
360 |
|
|
* Indicate there is nothing displayed yet. |
361 |
|
|
*/ |
362 |
|
37 |
pos_clear(); |
363 |
|
37 |
clr_linenum(); |
364 |
|
37 |
clr_hilite(); |
365 |
|
37 |
cmd_addhist(ml_examine, filename); |
366 |
✗✓ |
37 |
if (no_display && errmsgs > 0) { |
367 |
|
|
/* |
368 |
|
|
* We displayed some messages on error output |
369 |
|
|
* (file descriptor 2; see error() function). |
370 |
|
|
* Before erasing the screen contents, |
371 |
|
|
* display the file name and wait for a keystroke. |
372 |
|
|
*/ |
373 |
|
|
parg.p_string = filename; |
374 |
|
|
error("%s", &parg); |
375 |
|
|
} |
376 |
|
|
} |
377 |
|
37 |
free(filename); |
378 |
|
37 |
return (0); |
379 |
|
80 |
} |
380 |
|
|
|
381 |
|
|
/* |
382 |
|
|
* Edit a space-separated list of files. |
383 |
|
|
* For each filename in the list, enter it into the ifile list. |
384 |
|
|
* Then edit the first one. |
385 |
|
|
*/ |
386 |
|
|
int |
387 |
|
|
edit_list(char *filelist) |
388 |
|
|
{ |
389 |
|
|
IFILE save_ifile; |
390 |
|
|
char *good_filename; |
391 |
|
|
char *filename; |
392 |
|
|
char *gfilelist; |
393 |
|
|
char *gfilename; |
394 |
|
|
struct textlist tl_files; |
395 |
|
|
struct textlist tl_gfiles; |
396 |
|
|
|
397 |
|
|
save_ifile = save_curr_ifile(); |
398 |
|
|
good_filename = NULL; |
399 |
|
|
|
400 |
|
|
/* |
401 |
|
|
* Run thru each filename in the list. |
402 |
|
|
* Try to glob the filename. |
403 |
|
|
* If it doesn't expand, just try to open the filename. |
404 |
|
|
* If it does expand, try to open each name in that list. |
405 |
|
|
*/ |
406 |
|
|
init_textlist(&tl_files, filelist); |
407 |
|
|
filename = NULL; |
408 |
|
|
while ((filename = forw_textlist(&tl_files, filename)) != NULL) { |
409 |
|
|
gfilelist = lglob(filename); |
410 |
|
|
init_textlist(&tl_gfiles, gfilelist); |
411 |
|
|
gfilename = NULL; |
412 |
|
|
while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != |
413 |
|
|
NULL) { |
414 |
|
|
if (edit(gfilename) == 0 && good_filename == NULL) |
415 |
|
|
good_filename = get_filename(curr_ifile); |
416 |
|
|
} |
417 |
|
|
free(gfilelist); |
418 |
|
|
} |
419 |
|
|
/* |
420 |
|
|
* Edit the first valid filename in the list. |
421 |
|
|
*/ |
422 |
|
|
if (good_filename == NULL) { |
423 |
|
|
unsave_ifile(save_ifile); |
424 |
|
|
return (1); |
425 |
|
|
} |
426 |
|
|
if (get_ifile(good_filename, curr_ifile) == curr_ifile) { |
427 |
|
|
/* |
428 |
|
|
* Trying to edit the current file; don't reopen it. |
429 |
|
|
*/ |
430 |
|
|
unsave_ifile(save_ifile); |
431 |
|
|
return (0); |
432 |
|
|
} |
433 |
|
|
reedit_ifile(save_ifile); |
434 |
|
|
return (edit(good_filename)); |
435 |
|
|
} |
436 |
|
|
|
437 |
|
|
/* |
438 |
|
|
* Edit the first file in the command line (ifile) list. |
439 |
|
|
*/ |
440 |
|
|
int |
441 |
|
|
edit_first(void) |
442 |
|
|
{ |
443 |
|
54 |
curr_ifile = NULL; |
444 |
|
27 |
return (edit_next(1)); |
445 |
|
|
} |
446 |
|
|
|
447 |
|
|
/* |
448 |
|
|
* Edit the last file in the command line (ifile) list. |
449 |
|
|
*/ |
450 |
|
|
int |
451 |
|
|
edit_last(void) |
452 |
|
|
{ |
453 |
|
|
curr_ifile = NULL; |
454 |
|
|
return (edit_prev(1)); |
455 |
|
|
} |
456 |
|
|
|
457 |
|
|
|
458 |
|
|
/* |
459 |
|
|
* Edit the n-th next or previous file in the command line (ifile) list. |
460 |
|
|
*/ |
461 |
|
|
static int |
462 |
|
|
edit_istep(IFILE h, int n, int dir) |
463 |
|
|
{ |
464 |
|
|
IFILE next; |
465 |
|
|
|
466 |
|
|
/* |
467 |
|
|
* Skip n filenames, then try to edit each filename. |
468 |
|
|
*/ |
469 |
|
87 |
for (;;) { |
470 |
✓✗ |
171 |
next = (dir > 0) ? next_ifile(h) : prev_ifile(h); |
471 |
✓✓ |
57 |
if (--n < 0) { |
472 |
✓✓ |
27 |
if (edit_ifile(h) == 0) |
473 |
|
|
break; |
474 |
|
|
} |
475 |
✓✓ |
32 |
if (next == NULL) { |
476 |
|
|
/* |
477 |
|
|
* Reached end of the ifile list. |
478 |
|
|
*/ |
479 |
|
5 |
return (1); |
480 |
|
|
} |
481 |
✗✓ |
27 |
if (ABORT_SIGS()) { |
482 |
|
|
/* |
483 |
|
|
* Interrupt breaks out, if we're in a long |
484 |
|
|
* list of files that can't be opened. |
485 |
|
|
*/ |
486 |
|
|
return (1); |
487 |
|
|
} |
488 |
|
|
h = next; |
489 |
|
|
} |
490 |
|
|
/* |
491 |
|
|
* Found a file that we can edit. |
492 |
|
|
*/ |
493 |
|
25 |
return (0); |
494 |
|
30 |
} |
495 |
|
|
|
496 |
|
|
static int |
497 |
|
|
edit_inext(IFILE h, int n) |
498 |
|
|
{ |
499 |
|
|
return (edit_istep(h, n, +1)); |
500 |
|
|
} |
501 |
|
|
|
502 |
|
|
int |
503 |
|
|
edit_next(int n) |
504 |
|
|
{ |
505 |
|
60 |
return (edit_istep(curr_ifile, n, +1)); |
506 |
|
|
} |
507 |
|
|
|
508 |
|
|
static int |
509 |
|
|
edit_iprev(IFILE h, int n) |
510 |
|
|
{ |
511 |
|
|
return (edit_istep(h, n, -1)); |
512 |
|
|
} |
513 |
|
|
|
514 |
|
|
int |
515 |
|
|
edit_prev(int n) |
516 |
|
|
{ |
517 |
|
|
return (edit_istep(curr_ifile, n, -1)); |
518 |
|
|
} |
519 |
|
|
|
520 |
|
|
/* |
521 |
|
|
* Edit a specific file in the command line (ifile) list. |
522 |
|
|
*/ |
523 |
|
|
int |
524 |
|
|
edit_index(int n) |
525 |
|
|
{ |
526 |
|
|
IFILE h; |
527 |
|
|
|
528 |
|
|
h = NULL; |
529 |
|
|
do { |
530 |
|
|
if ((h = next_ifile(h)) == NULL) { |
531 |
|
|
/* |
532 |
|
|
* Reached end of the list without finding it. |
533 |
|
|
*/ |
534 |
|
|
return (1); |
535 |
|
|
} |
536 |
|
|
} while (get_index(h) != n); |
537 |
|
|
|
538 |
|
|
return (edit_ifile(h)); |
539 |
|
|
} |
540 |
|
|
|
541 |
|
|
IFILE |
542 |
|
|
save_curr_ifile(void) |
543 |
|
|
{ |
544 |
✓✓ |
174 |
if (curr_ifile != NULL) |
545 |
|
48 |
hold_ifile(curr_ifile, 1); |
546 |
|
87 |
return (curr_ifile); |
547 |
|
|
} |
548 |
|
|
|
549 |
|
|
void |
550 |
|
|
unsave_ifile(IFILE save_ifile) |
551 |
|
|
{ |
552 |
✓✓ |
100 |
if (save_ifile != NULL) |
553 |
|
48 |
hold_ifile(save_ifile, -1); |
554 |
|
50 |
} |
555 |
|
|
|
556 |
|
|
/* |
557 |
|
|
* Reedit the ifile which was previously open. |
558 |
|
|
*/ |
559 |
|
|
void |
560 |
|
|
reedit_ifile(IFILE save_ifile) |
561 |
|
|
{ |
562 |
|
|
IFILE next; |
563 |
|
|
IFILE prev; |
564 |
|
|
|
565 |
|
|
/* |
566 |
|
|
* Try to reopen the ifile. |
567 |
|
|
* Note that opening it may fail (maybe the file was removed), |
568 |
|
|
* in which case the ifile will be deleted from the list. |
569 |
|
|
* So save the next and prev ifiles first. |
570 |
|
|
*/ |
571 |
|
4 |
unsave_ifile(save_ifile); |
572 |
|
2 |
next = next_ifile(save_ifile); |
573 |
|
2 |
prev = prev_ifile(save_ifile); |
574 |
✓✗ |
2 |
if (edit_ifile(save_ifile) == 0) |
575 |
|
2 |
return; |
576 |
|
|
/* |
577 |
|
|
* If can't reopen it, open the next input file in the list. |
578 |
|
|
*/ |
579 |
|
|
if (next != NULL && edit_inext(next, 0) == 0) |
580 |
|
|
return; |
581 |
|
|
/* |
582 |
|
|
* If can't open THAT one, open the previous input file in the list. |
583 |
|
|
*/ |
584 |
|
|
if (prev != NULL && edit_iprev(prev, 0) == 0) |
585 |
|
|
return; |
586 |
|
|
/* |
587 |
|
|
* If can't even open that, we're stuck. Just quit. |
588 |
|
|
*/ |
589 |
|
|
quit(QUIT_ERROR); |
590 |
|
2 |
} |
591 |
|
|
|
592 |
|
|
void |
593 |
|
|
reopen_curr_ifile(void) |
594 |
|
|
{ |
595 |
|
|
IFILE save_ifile = save_curr_ifile(); |
596 |
|
|
close_file(); |
597 |
|
|
reedit_ifile(save_ifile); |
598 |
|
|
} |
599 |
|
|
|
600 |
|
|
/* |
601 |
|
|
* Edit standard input. |
602 |
|
|
*/ |
603 |
|
|
int |
604 |
|
|
edit_stdin(void) |
605 |
|
|
{ |
606 |
✗✓ |
24 |
if (isatty(fd0)) { |
607 |
|
|
if (less_is_more) { |
608 |
|
|
error("Missing filename (\"more -h\" for help)", |
609 |
|
|
NULL); |
610 |
|
|
} else { |
611 |
|
|
error("Missing filename (\"less --help\" for help)", |
612 |
|
|
NULL); |
613 |
|
|
} |
614 |
|
|
quit(QUIT_OK); |
615 |
|
|
} |
616 |
|
12 |
return (edit("-")); |
617 |
|
|
} |
618 |
|
|
|
619 |
|
|
/* |
620 |
|
|
* Copy a file directly to standard output. |
621 |
|
|
* Used if standard output is not a tty. |
622 |
|
|
*/ |
623 |
|
|
void |
624 |
|
|
cat_file(void) |
625 |
|
|
{ |
626 |
|
|
int c; |
627 |
|
|
|
628 |
|
|
while ((c = ch_forw_get()) != EOI) |
629 |
|
|
putchr(c); |
630 |
|
|
flush(0); |
631 |
|
|
} |
632 |
|
|
|
633 |
|
|
/* |
634 |
|
|
* If the user asked for a log file and our input file |
635 |
|
|
* is standard input, create the log file. |
636 |
|
|
* We take care not to blindly overwrite an existing file. |
637 |
|
|
*/ |
638 |
|
|
void |
639 |
|
|
use_logfile(char *filename) |
640 |
|
|
{ |
641 |
|
|
int exists; |
642 |
|
|
int answer; |
643 |
|
|
PARG parg; |
644 |
|
|
|
645 |
|
|
if (ch_getflags() & CH_CANSEEK) |
646 |
|
|
/* |
647 |
|
|
* Can't currently use a log file on a file that can seek. |
648 |
|
|
*/ |
649 |
|
|
return; |
650 |
|
|
|
651 |
|
|
/* |
652 |
|
|
* {{ We could use access() here. }} |
653 |
|
|
*/ |
654 |
|
|
filename = shell_unquote(filename); |
655 |
|
|
exists = open(filename, O_RDONLY); |
656 |
|
|
close(exists); |
657 |
|
|
exists = (exists >= 0); |
658 |
|
|
|
659 |
|
|
/* |
660 |
|
|
* Decide whether to overwrite the log file or append to it. |
661 |
|
|
* If it doesn't exist we "overwrite" it. |
662 |
|
|
*/ |
663 |
|
|
if (!exists || force_logfile) { |
664 |
|
|
/* |
665 |
|
|
* Overwrite (or create) the log file. |
666 |
|
|
*/ |
667 |
|
|
answer = 'O'; |
668 |
|
|
} else { |
669 |
|
|
/* |
670 |
|
|
* Ask user what to do. |
671 |
|
|
*/ |
672 |
|
|
parg.p_string = filename; |
673 |
|
|
answer = query("Warning: \"%s\" exists; " |
674 |
|
|
"Overwrite, Append or Don't log? ", &parg); |
675 |
|
|
} |
676 |
|
|
|
677 |
|
|
loop: |
678 |
|
|
switch (answer) { |
679 |
|
|
case 'O': case 'o': |
680 |
|
|
/* |
681 |
|
|
* Overwrite: create the file. |
682 |
|
|
*/ |
683 |
|
|
logfile = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0644); |
684 |
|
|
break; |
685 |
|
|
case 'A': case 'a': |
686 |
|
|
/* |
687 |
|
|
* Append: open the file and seek to the end. |
688 |
|
|
*/ |
689 |
|
|
logfile = open(filename, O_WRONLY | O_APPEND); |
690 |
|
|
if (lseek(logfile, (off_t)0, SEEK_END) == (off_t)-1) { |
691 |
|
|
close(logfile); |
692 |
|
|
logfile = -1; |
693 |
|
|
} |
694 |
|
|
break; |
695 |
|
|
case 'D': case 'd': |
696 |
|
|
/* |
697 |
|
|
* Don't do anything. |
698 |
|
|
*/ |
699 |
|
|
free(filename); |
700 |
|
|
return; |
701 |
|
|
case 'q': |
702 |
|
|
quit(QUIT_OK); |
703 |
|
|
default: |
704 |
|
|
/* |
705 |
|
|
* Eh? |
706 |
|
|
*/ |
707 |
|
|
answer = query("Overwrite, Append, or Don't log? " |
708 |
|
|
"(Type \"O\", \"A\", \"D\" or \"q\") ", NULL); |
709 |
|
|
goto loop; |
710 |
|
|
} |
711 |
|
|
|
712 |
|
|
if (logfile < 0) { |
713 |
|
|
/* |
714 |
|
|
* Error in opening logfile. |
715 |
|
|
*/ |
716 |
|
|
parg.p_string = filename; |
717 |
|
|
error("Cannot write to \"%s\"", &parg); |
718 |
|
|
free(filename); |
719 |
|
|
return; |
720 |
|
|
} |
721 |
|
|
free(filename); |
722 |
|
|
} |