Thu Feb 24 10:07:47 2022 UTC ()
Pull up following revision(s) (requested by kre in ticket #1736):

	bin/sh/histedit.c: revision 1.60

After (a few days short of) 21 years, revert 1.25, which did nothing except
make the -e option to "fc" fail to work (the commit message was about some
other changes entirely, so I an only assume this was committed by mistake).

It says a lot about the use of the fc command that no-one noticed that
this did not work properly for all this time.

Internally in sh, it is possible for built in commands to use either
getopt(3) (from libc) or the much simpler internal shell nextopt() routine
for option (flag) parsing.    However it makes no sense to use getopt()
and then access a global variable set only by nextopt() instead of the
one getopt() sets (which is what the code had used previously, forever).

Use the correct variable again.

XXX pullup -9 -8  (-7 -6 -5 ...)


(martin)
diff -r1.48.8.2 -r1.48.8.3 src/bin/sh/histedit.c

cvs diff -r1.48.8.2 -r1.48.8.3 src/bin/sh/histedit.c (switch to unified diff)

--- src/bin/sh/histedit.c 2018/08/25 14:45:37 1.48.8.2
+++ src/bin/sh/histedit.c 2022/02/24 10:07:46 1.48.8.3
@@ -1,576 +1,576 @@ @@ -1,576 +1,576 @@
1/* $NetBSD: histedit.c,v 1.48.8.2 2018/08/25 14:45:37 martin Exp $ */ 1/* $NetBSD: histedit.c,v 1.48.8.3 2022/02/24 10:07:46 martin Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c) 1993 4 * Copyright (c) 1993
5 * The Regents of the University of California. All rights reserved. 5 * The Regents of the University of California. All rights reserved.
6 * 6 *
7 * This code is derived from software contributed to Berkeley by 7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist. 8 * Kenneth Almquist.
9 * 9 *
10 * Redistribution and use in source and binary forms, with or without 10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions 11 * modification, are permitted provided that the following conditions
12 * are met: 12 * are met:
13 * 1. Redistributions of source code must retain the above copyright 13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer. 14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright 15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the 16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution. 17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors 18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software 19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission. 20 * without specific prior written permission.
21 * 21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE. 32 * SUCH DAMAGE.
33 */ 33 */
34 34
35#include <sys/cdefs.h> 35#include <sys/cdefs.h>
36#ifndef lint 36#ifndef lint
37#if 0 37#if 0
38static char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95"; 38static char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95";
39#else 39#else
40__RCSID("$NetBSD: histedit.c,v 1.48.8.2 2018/08/25 14:45:37 martin Exp $"); 40__RCSID("$NetBSD: histedit.c,v 1.48.8.3 2022/02/24 10:07:46 martin Exp $");
41#endif 41#endif
42#endif /* not lint */ 42#endif /* not lint */
43 43
44#include <sys/param.h> 44#include <sys/param.h>
45#include <paths.h> 45#include <paths.h>
46#include <stdio.h> 46#include <stdio.h>
47#include <stdlib.h> 47#include <stdlib.h>
48#include <unistd.h> 48#include <unistd.h>
49/* 49/*
50 * Editline and history functions (and glue). 50 * Editline and history functions (and glue).
51 */ 51 */
52#include "shell.h" 52#include "shell.h"
53#include "parser.h" 53#include "parser.h"
54#include "var.h" 54#include "var.h"
55#include "options.h" 55#include "options.h"
56#include "builtins.h" 56#include "builtins.h"
57#include "main.h" 57#include "main.h"
58#include "output.h" 58#include "output.h"
59#include "mystring.h" 59#include "mystring.h"
60#include "myhistedit.h" 60#include "myhistedit.h"
61#include "error.h" 61#include "error.h"
62#include "alias.h" 62#include "alias.h"
63#ifndef SMALL 63#ifndef SMALL
64#include "eval.h" 64#include "eval.h"
65#include "memalloc.h" 65#include "memalloc.h"
66 66
67#define MAXHISTLOOPS 4 /* max recursions through fc */ 67#define MAXHISTLOOPS 4 /* max recursions through fc */
68#define DEFEDITOR "ed" /* default editor *should* be $EDITOR */ 68#define DEFEDITOR "ed" /* default editor *should* be $EDITOR */
69 69
70History *hist; /* history cookie */ 70History *hist; /* history cookie */
71EditLine *el; /* editline cookie */ 71EditLine *el; /* editline cookie */
72int displayhist; 72int displayhist;
73static FILE *el_in, *el_out; 73static FILE *el_in, *el_out;
74unsigned char _el_fn_complete(EditLine *, int); 74unsigned char _el_fn_complete(EditLine *, int);
75 75
76STATIC const char *fc_replace(const char *, char *, char *); 76STATIC const char *fc_replace(const char *, char *, char *);
77 77
78#ifdef DEBUG 78#ifdef DEBUG
79extern FILE *tracefile; 79extern FILE *tracefile;
80#endif 80#endif
81 81
82/* 82/*
83 * Set history and editing status. Called whenever the status may 83 * Set history and editing status. Called whenever the status may
84 * have changed (figures out what to do). 84 * have changed (figures out what to do).
85 */ 85 */
86void 86void
87histedit(void) 87histedit(void)
88{ 88{
89 FILE *el_err; 89 FILE *el_err;
90 90
91#define editing (Eflag || Vflag) 91#define editing (Eflag || Vflag)
92 92
93 if (iflag == 1) { 93 if (iflag == 1) {
94 if (!hist) { 94 if (!hist) {
95 /* 95 /*
96 * turn history on 96 * turn history on
97 */ 97 */
98 INTOFF; 98 INTOFF;
99 hist = history_init(); 99 hist = history_init();
100 INTON; 100 INTON;
101 101
102 if (hist != NULL) 102 if (hist != NULL)
103 sethistsize(histsizeval()); 103 sethistsize(histsizeval());
104 else 104 else
105 out2str("sh: can't initialize history\n"); 105 out2str("sh: can't initialize history\n");
106 } 106 }
107 if (editing && !el && isatty(0)) { /* && isatty(2) ??? */ 107 if (editing && !el && isatty(0)) { /* && isatty(2) ??? */
108 /* 108 /*
109 * turn editing on 109 * turn editing on
110 */ 110 */
111 char *term, *shname; 111 char *term, *shname;
112 112
113 INTOFF; 113 INTOFF;
114 if (el_in == NULL) 114 if (el_in == NULL)
115 el_in = fdopen(0, "r"); 115 el_in = fdopen(0, "r");
116 if (el_out == NULL) 116 if (el_out == NULL)
117 el_out = fdopen(2, "w"); 117 el_out = fdopen(2, "w");
118 if (el_in == NULL || el_out == NULL) 118 if (el_in == NULL || el_out == NULL)
119 goto bad; 119 goto bad;
120 el_err = el_out; 120 el_err = el_out;
121#if DEBUG 121#if DEBUG
122 if (tracefile) 122 if (tracefile)
123 el_err = tracefile; 123 el_err = tracefile;
124#endif 124#endif
125 term = lookupvar("TERM"); 125 term = lookupvar("TERM");
126 if (term) 126 if (term)
127 setenv("TERM", term, 1); 127 setenv("TERM", term, 1);
128 else 128 else
129 unsetenv("TERM"); 129 unsetenv("TERM");
130 shname = arg0; 130 shname = arg0;
131 if (shname[0] == '-') 131 if (shname[0] == '-')
132 shname++; 132 shname++;
133 el = el_init(shname, el_in, el_out, el_err); 133 el = el_init(shname, el_in, el_out, el_err);
134 if (el != NULL) { 134 if (el != NULL) {
135 if (hist) 135 if (hist)
136 el_set(el, EL_HIST, history, hist); 136 el_set(el, EL_HIST, history, hist);
137 137
138 set_prompt_lit(lookupvar("PSlit")); 138 set_prompt_lit(lookupvar("PSlit"));
139 el_set(el, EL_SIGNAL, 1); 139 el_set(el, EL_SIGNAL, 1);
140 el_set(el, EL_ALIAS_TEXT, alias_text, NULL); 140 el_set(el, EL_ALIAS_TEXT, alias_text, NULL);
141 el_set(el, EL_ADDFN, "rl-complete", 141 el_set(el, EL_ADDFN, "rl-complete",
142 "ReadLine compatible completion function", 142 "ReadLine compatible completion function",
143 _el_fn_complete); 143 _el_fn_complete);
144 } else { 144 } else {
145bad: 145bad:
146 out2str("sh: can't initialize editing\n"); 146 out2str("sh: can't initialize editing\n");
147 } 147 }
148 INTON; 148 INTON;
149 } else if (!editing && el) { 149 } else if (!editing && el) {
150 INTOFF; 150 INTOFF;
151 el_end(el); 151 el_end(el);
152 el = NULL; 152 el = NULL;
153 INTON; 153 INTON;
154 } 154 }
155 if (el) { 155 if (el) {
156 if (Vflag) 156 if (Vflag)
157 el_set(el, EL_EDITOR, "vi"); 157 el_set(el, EL_EDITOR, "vi");
158 else if (Eflag) 158 else if (Eflag)
159 el_set(el, EL_EDITOR, "emacs"); 159 el_set(el, EL_EDITOR, "emacs");
160 el_set(el, EL_BIND, "^I",  160 el_set(el, EL_BIND, "^I",
161 tabcomplete ? "rl-complete" : "ed-insert", NULL); 161 tabcomplete ? "rl-complete" : "ed-insert", NULL);
162 el_source(el, lookupvar("EDITRC")); 162 el_source(el, lookupvar("EDITRC"));
163 } 163 }
164 } else { 164 } else {
165 INTOFF; 165 INTOFF;
166 if (el) { /* no editing if not interactive */ 166 if (el) { /* no editing if not interactive */
167 el_end(el); 167 el_end(el);
168 el = NULL; 168 el = NULL;
169 } 169 }
170 if (hist) { 170 if (hist) {
171 history_end(hist); 171 history_end(hist);
172 hist = NULL; 172 hist = NULL;
173 } 173 }
174 INTON; 174 INTON;
175 } 175 }
176} 176}
177 177
178void 178void
179set_prompt_lit(const char *lit_ch) 179set_prompt_lit(const char *lit_ch)
180{ 180{
181 wchar_t wc; 181 wchar_t wc;
182 182
183 if (!(iflag && editing && el)) 183 if (!(iflag && editing && el))
184 return; 184 return;
185 185
186 if (lit_ch == NULL) { 186 if (lit_ch == NULL) {
187 el_set(el, EL_PROMPT, getprompt); 187 el_set(el, EL_PROMPT, getprompt);
188 return; 188 return;
189 } 189 }
190 190
191 mbtowc(&wc, NULL, 1); /* state init */ 191 mbtowc(&wc, NULL, 1); /* state init */
192 192
193 if (mbtowc(&wc, lit_ch, strlen(lit_ch)) <= 0) 193 if (mbtowc(&wc, lit_ch, strlen(lit_ch)) <= 0)
194 el_set(el, EL_PROMPT, getprompt); 194 el_set(el, EL_PROMPT, getprompt);
195 else 195 else
196 el_set(el, EL_PROMPT_ESC, getprompt, (int)wc); 196 el_set(el, EL_PROMPT_ESC, getprompt, (int)wc);
197} 197}
198 198
199void 199void
200set_editrc(const char *fname) 200set_editrc(const char *fname)
201{ 201{
202 if (iflag && editing && el) 202 if (iflag && editing && el)
203 el_source(el, fname); 203 el_source(el, fname);
204} 204}
205 205
206void 206void
207sethistsize(const char *hs) 207sethistsize(const char *hs)
208{ 208{
209 int histsize; 209 int histsize;
210 HistEvent he; 210 HistEvent he;
211 211
212 if (hist != NULL) { 212 if (hist != NULL) {
213 if (hs == NULL || *hs == '\0' || *hs == '-' || 213 if (hs == NULL || *hs == '\0' || *hs == '-' ||
214 (histsize = number(hs)) < 0) 214 (histsize = number(hs)) < 0)
215 histsize = 100; 215 histsize = 100;
216 history(hist, &he, H_SETSIZE, histsize); 216 history(hist, &he, H_SETSIZE, histsize);
217 history(hist, &he, H_SETUNIQUE, 1); 217 history(hist, &he, H_SETUNIQUE, 1);
218 } 218 }
219} 219}
220 220
221void 221void
222setterm(const char *term) 222setterm(const char *term)
223{ 223{
224 if (el != NULL && term != NULL) 224 if (el != NULL && term != NULL)
225 if (el_set(el, EL_TERMINAL, term) != 0) { 225 if (el_set(el, EL_TERMINAL, term) != 0) {
226 outfmt(out2, "sh: Can't set terminal type %s\n", term); 226 outfmt(out2, "sh: Can't set terminal type %s\n", term);
227 outfmt(out2, "sh: Using dumb terminal settings.\n"); 227 outfmt(out2, "sh: Using dumb terminal settings.\n");
228 } 228 }
229} 229}
230 230
231int 231int
232inputrc(int argc, char **argv) 232inputrc(int argc, char **argv)
233{ 233{
234 if (argc != 2) { 234 if (argc != 2) {
235 out2str("usage: inputrc file\n"); 235 out2str("usage: inputrc file\n");
236 return 1; 236 return 1;
237 } 237 }
238 if (el != NULL) { 238 if (el != NULL) {
239 if (el_source(el, argv[1])) { 239 if (el_source(el, argv[1])) {
240 out2str("inputrc: failed\n"); 240 out2str("inputrc: failed\n");
241 return 1; 241 return 1;
242 } else 242 } else
243 return 0; 243 return 0;
244 } else { 244 } else {
245 out2str("sh: inputrc ignored, not editing\n"); 245 out2str("sh: inputrc ignored, not editing\n");
246 return 1; 246 return 1;
247 } 247 }
248} 248}
249 249
250/* 250/*
251 * This command is provided since POSIX decided to standardize 251 * This command is provided since POSIX decided to standardize
252 * the Korn shell fc command. Oh well... 252 * the Korn shell fc command. Oh well...
253 */ 253 */
254int 254int
255histcmd(volatile int argc, char ** volatile argv) 255histcmd(volatile int argc, char ** volatile argv)
256{ 256{
257 int ch; 257 int ch;
258 const char * volatile editor = NULL; 258 const char * volatile editor = NULL;
259 HistEvent he; 259 HistEvent he;
260 volatile int lflg = 0, nflg = 0, rflg = 0, sflg = 0; 260 volatile int lflg = 0, nflg = 0, rflg = 0, sflg = 0;
261 int i, retval; 261 int i, retval;
262 const char *firststr, *laststr; 262 const char *firststr, *laststr;
263 int first, last, direction; 263 int first, last, direction;
264 char * volatile pat = NULL, * volatile repl; /* ksh "fc old=new" crap */ 264 char * volatile pat = NULL, * volatile repl; /* ksh "fc old=new" crap */
265 static int active = 0; 265 static int active = 0;
266 struct jmploc jmploc; 266 struct jmploc jmploc;
267 struct jmploc *volatile savehandler; 267 struct jmploc *volatile savehandler;
268 char editfile[MAXPATHLEN + 1]; 268 char editfile[MAXPATHLEN + 1];
269 FILE * volatile efp; 269 FILE * volatile efp;
270#ifdef __GNUC__ 270#ifdef __GNUC__
271 repl = NULL; /* XXX gcc4 */ 271 repl = NULL; /* XXX gcc4 */
272 efp = NULL; /* XXX gcc4 */ 272 efp = NULL; /* XXX gcc4 */
273#endif 273#endif
274 274
275 if (hist == NULL) 275 if (hist == NULL)
276 error("history not active"); 276 error("history not active");
277 277
278 if (argc == 1) 278 if (argc == 1)
279 error("missing history argument"); 279 error("missing history argument");
280 280
281 optreset = 1; optind = 1; /* initialize getopt */ 281 optreset = 1; optind = 1; /* initialize getopt */
282 while (not_fcnumber(argv[optind]) && 282 while (not_fcnumber(argv[optind]) &&
283 (ch = getopt(argc, argv, ":e:lnrs")) != -1) 283 (ch = getopt(argc, argv, ":e:lnrs")) != -1)
284 switch ((char)ch) { 284 switch ((char)ch) {
285 case 'e': 285 case 'e':
286 editor = optionarg; 286 editor = optarg;
287 break; 287 break;
288 case 'l': 288 case 'l':
289 lflg = 1; 289 lflg = 1;
290 break; 290 break;
291 case 'n': 291 case 'n':
292 nflg = 1; 292 nflg = 1;
293 break; 293 break;
294 case 'r': 294 case 'r':
295 rflg = 1; 295 rflg = 1;
296 break; 296 break;
297 case 's': 297 case 's':
298 sflg = 1; 298 sflg = 1;
299 break; 299 break;
300 case ':': 300 case ':':
301 error("option -%c expects argument", optopt); 301 error("option -%c expects argument", optopt);
302 /* NOTREACHED */ 302 /* NOTREACHED */
303 case '?': 303 case '?':
304 default: 304 default:
305 error("unknown option: -%c", optopt); 305 error("unknown option: -%c", optopt);
306 /* NOTREACHED */ 306 /* NOTREACHED */
307 } 307 }
308 argc -= optind, argv += optind; 308 argc -= optind, argv += optind;
309 309
310 /* 310 /*
311 * If executing... 311 * If executing...
312 */ 312 */
313 if (lflg == 0 || editor || sflg) { 313 if (lflg == 0 || editor || sflg) {
314 lflg = 0; /* ignore */ 314 lflg = 0; /* ignore */
315 editfile[0] = '\0'; 315 editfile[0] = '\0';
316 /* 316 /*
317 * Catch interrupts to reset active counter and 317 * Catch interrupts to reset active counter and
318 * cleanup temp files. 318 * cleanup temp files.
319 */ 319 */
320 savehandler = handler; 320 savehandler = handler;
321 if (setjmp(jmploc.loc)) { 321 if (setjmp(jmploc.loc)) {
322 active = 0; 322 active = 0;
323 if (*editfile) 323 if (*editfile)
324 unlink(editfile); 324 unlink(editfile);
325 handler = savehandler; 325 handler = savehandler;
326 longjmp(handler->loc, 1); 326 longjmp(handler->loc, 1);
327 } 327 }
328 handler = &jmploc; 328 handler = &jmploc;
329 if (++active > MAXHISTLOOPS) { 329 if (++active > MAXHISTLOOPS) {
330 active = 0; 330 active = 0;
331 displayhist = 0; 331 displayhist = 0;
332 error("called recursively too many times"); 332 error("called recursively too many times");
333 } 333 }
334 /* 334 /*
335 * Set editor. 335 * Set editor.
336 */ 336 */
337 if (sflg == 0) { 337 if (sflg == 0) {
338 if (editor == NULL && 338 if (editor == NULL &&
339 (editor = bltinlookup("FCEDIT", 1)) == NULL && 339 (editor = bltinlookup("FCEDIT", 1)) == NULL &&
340 (editor = bltinlookup("EDITOR", 1)) == NULL) 340 (editor = bltinlookup("EDITOR", 1)) == NULL)
341 editor = DEFEDITOR; 341 editor = DEFEDITOR;
342 if (editor[0] == '-' && editor[1] == '\0') { 342 if (editor[0] == '-' && editor[1] == '\0') {
343 sflg = 1; /* no edit */ 343 sflg = 1; /* no edit */
344 editor = NULL; 344 editor = NULL;
345 } 345 }
346 } 346 }
347 } 347 }
348 348
349 /* 349 /*
350 * If executing, parse [old=new] now 350 * If executing, parse [old=new] now
351 */ 351 */
352 if (lflg == 0 && argc > 0 && 352 if (lflg == 0 && argc > 0 &&
353 ((repl = strchr(argv[0], '=')) != NULL)) { 353 ((repl = strchr(argv[0], '=')) != NULL)) {
354 pat = argv[0]; 354 pat = argv[0];
355 *repl++ = '\0'; 355 *repl++ = '\0';
356 argc--, argv++; 356 argc--, argv++;
357 } 357 }
358 358
359 /* 359 /*
360 * If -s is specified, accept only one operand 360 * If -s is specified, accept only one operand
361 */ 361 */
362 if (sflg && argc >= 2) 362 if (sflg && argc >= 2)
363 error("too many args"); 363 error("too many args");
364 364
365 /* 365 /*
366 * determine [first] and [last] 366 * determine [first] and [last]
367 */ 367 */
368 switch (argc) { 368 switch (argc) {
369 case 0: 369 case 0:
370 firststr = lflg ? "-16" : "-1"; 370 firststr = lflg ? "-16" : "-1";
371 laststr = "-1"; 371 laststr = "-1";
372 break; 372 break;
373 case 1: 373 case 1:
374 firststr = argv[0]; 374 firststr = argv[0];
375 laststr = lflg ? "-1" : argv[0]; 375 laststr = lflg ? "-1" : argv[0];
376 break; 376 break;
377 case 2: 377 case 2:
378 firststr = argv[0]; 378 firststr = argv[0];
379 laststr = argv[1]; 379 laststr = argv[1];
380 break; 380 break;
381 default: 381 default:
382 error("too many args"); 382 error("too many args");
383 /* NOTREACHED */ 383 /* NOTREACHED */
384 } 384 }
385 /* 385 /*
386 * Turn into event numbers. 386 * Turn into event numbers.
387 */ 387 */
388 first = str_to_event(firststr, 0); 388 first = str_to_event(firststr, 0);
389 last = str_to_event(laststr, 1); 389 last = str_to_event(laststr, 1);
390 390
391 if (rflg) { 391 if (rflg) {
392 i = last; 392 i = last;
393 last = first; 393 last = first;
394 first = i; 394 first = i;
395 } 395 }
396 /* 396 /*
397 * XXX - this should not depend on the event numbers 397 * XXX - this should not depend on the event numbers
398 * always increasing. Add sequence numbers or offset 398 * always increasing. Add sequence numbers or offset
399 * to the history element in next (diskbased) release. 399 * to the history element in next (diskbased) release.
400 */ 400 */
401 direction = first < last ? H_PREV : H_NEXT; 401 direction = first < last ? H_PREV : H_NEXT;
402 402
403 /* 403 /*
404 * If editing, grab a temp file. 404 * If editing, grab a temp file.
405 */ 405 */
406 if (editor) { 406 if (editor) {
407 int fd; 407 int fd;
408 INTOFF; /* easier */ 408 INTOFF; /* easier */
409 snprintf(editfile, sizeof(editfile), "%s_shXXXXXX", _PATH_TMP); 409 snprintf(editfile, sizeof(editfile), "%s_shXXXXXX", _PATH_TMP);
410 if ((fd = mkstemp(editfile)) < 0) 410 if ((fd = mkstemp(editfile)) < 0)
411 error("can't create temporary file %s", editfile); 411 error("can't create temporary file %s", editfile);
412 if ((efp = fdopen(fd, "w")) == NULL) { 412 if ((efp = fdopen(fd, "w")) == NULL) {
413 close(fd); 413 close(fd);
414 error("can't allocate stdio buffer for temp"); 414 error("can't allocate stdio buffer for temp");
415 } 415 }
416 } 416 }
417 417
418 /* 418 /*
419 * Loop through selected history events. If listing or executing, 419 * Loop through selected history events. If listing or executing,
420 * do it now. Otherwise, put into temp file and call the editor 420 * do it now. Otherwise, put into temp file and call the editor
421 * after. 421 * after.
422 * 422 *
423 * The history interface needs rethinking, as the following 423 * The history interface needs rethinking, as the following
424 * convolutions will demonstrate. 424 * convolutions will demonstrate.
425 */ 425 */
426 history(hist, &he, H_FIRST); 426 history(hist, &he, H_FIRST);
427 retval = history(hist, &he, H_NEXT_EVENT, first); 427 retval = history(hist, &he, H_NEXT_EVENT, first);
428 for (;retval != -1; retval = history(hist, &he, direction)) { 428 for (;retval != -1; retval = history(hist, &he, direction)) {
429 if (lflg) { 429 if (lflg) {
430 if (!nflg) 430 if (!nflg)
431 out1fmt("%5d ", he.num); 431 out1fmt("%5d ", he.num);
432 out1str(he.str); 432 out1str(he.str);
433 } else { 433 } else {
434 const char *s = pat ? 434 const char *s = pat ?
435 fc_replace(he.str, pat, repl) : he.str; 435 fc_replace(he.str, pat, repl) : he.str;
436 436
437 if (sflg) { 437 if (sflg) {
438 if (displayhist) { 438 if (displayhist) {
439 out2str(s); 439 out2str(s);
440 } 440 }
441 441
442 evalstring(strcpy(stalloc(strlen(s) + 1), s), 0); 442 evalstring(strcpy(stalloc(strlen(s) + 1), s), 0);
443 if (displayhist && hist) { 443 if (displayhist && hist) {
444 /* 444 /*
445 * XXX what about recursive and 445 * XXX what about recursive and
446 * relative histnums. 446 * relative histnums.
447 */ 447 */
448 history(hist, &he, H_ENTER, s); 448 history(hist, &he, H_ENTER, s);
449 } 449 }
450 450
451 break; 451 break;
452 } else 452 } else
453 fputs(s, efp); 453 fputs(s, efp);
454 } 454 }
455 /* 455 /*
456 * At end? (if we were to lose last, we'd sure be 456 * At end? (if we were to lose last, we'd sure be
457 * messed up). 457 * messed up).
458 */ 458 */
459 if (he.num == last) 459 if (he.num == last)
460 break; 460 break;
461 } 461 }
462 if (editor) { 462 if (editor) {
463 char *editcmd; 463 char *editcmd;
464 size_t cmdlen; 464 size_t cmdlen;
465 465
466 fclose(efp); 466 fclose(efp);
467 cmdlen = strlen(editor) + strlen(editfile) + 2; 467 cmdlen = strlen(editor) + strlen(editfile) + 2;
468 editcmd = stalloc(cmdlen); 468 editcmd = stalloc(cmdlen);
469 snprintf(editcmd, cmdlen, "%s %s", editor, editfile); 469 snprintf(editcmd, cmdlen, "%s %s", editor, editfile);
470 evalstring(editcmd, 0); /* XXX - should use no JC command */ 470 evalstring(editcmd, 0); /* XXX - should use no JC command */
471 INTON; 471 INTON;
472 readcmdfile(editfile); /* XXX - should read back - quick tst */ 472 readcmdfile(editfile); /* XXX - should read back - quick tst */
473 unlink(editfile); 473 unlink(editfile);
474 } 474 }
475 475
476 if (lflg == 0 && active > 0) 476 if (lflg == 0 && active > 0)
477 --active; 477 --active;
478 if (displayhist) 478 if (displayhist)
479 displayhist = 0; 479 displayhist = 0;
480 return 0; 480 return 0;
481} 481}
482 482
483STATIC const char * 483STATIC const char *
484fc_replace(const char *s, char *p, char *r) 484fc_replace(const char *s, char *p, char *r)
485{ 485{
486 char *dest; 486 char *dest;
487 int plen = strlen(p); 487 int plen = strlen(p);
488 488
489 STARTSTACKSTR(dest); 489 STARTSTACKSTR(dest);
490 while (*s) { 490 while (*s) {
491 if (*s == *p && strncmp(s, p, plen) == 0) { 491 if (*s == *p && strncmp(s, p, plen) == 0) {
492 while (*r) 492 while (*r)
493 STPUTC(*r++, dest); 493 STPUTC(*r++, dest);
494 s += plen; 494 s += plen;
495 *p = '\0'; /* so no more matches */ 495 *p = '\0'; /* so no more matches */
496 } else 496 } else
497 STPUTC(*s++, dest); 497 STPUTC(*s++, dest);
498 } 498 }
499 STACKSTRNUL(dest); 499 STACKSTRNUL(dest);
500 dest = grabstackstr(dest); 500 dest = grabstackstr(dest);
501 501
502 return (dest); 502 return (dest);
503} 503}
504 504
505int 505int
506not_fcnumber(char *s) 506not_fcnumber(char *s)
507{ 507{
508 if (s == NULL) 508 if (s == NULL)
509 return 0; 509 return 0;
510 if (*s == '-') 510 if (*s == '-')
511 s++; 511 s++;
512 return (!is_number(s)); 512 return (!is_number(s));
513} 513}
514 514
515int 515int
516str_to_event(const char *str, int last) 516str_to_event(const char *str, int last)
517{ 517{
518 HistEvent he; 518 HistEvent he;
519 const char *s = str; 519 const char *s = str;
520 int relative = 0; 520 int relative = 0;
521 int i, retval; 521 int i, retval;
522 522
523 retval = history(hist, &he, H_FIRST); 523 retval = history(hist, &he, H_FIRST);
524 switch (*s) { 524 switch (*s) {
525 case '-': 525 case '-':
526 relative = 1; 526 relative = 1;
527 /*FALLTHROUGH*/ 527 /*FALLTHROUGH*/
528 case '+': 528 case '+':
529 s++; 529 s++;
530 } 530 }
531 if (is_number(s)) { 531 if (is_number(s)) {
532 i = number(s); 532 i = number(s);
533 if (relative) { 533 if (relative) {
534 while (retval != -1 && i--) { 534 while (retval != -1 && i--) {
535 retval = history(hist, &he, H_NEXT); 535 retval = history(hist, &he, H_NEXT);
536 } 536 }
537 if (retval == -1) 537 if (retval == -1)
538 retval = history(hist, &he, H_LAST); 538 retval = history(hist, &he, H_LAST);
539 } else { 539 } else {
540 retval = history(hist, &he, H_NEXT_EVENT, i); 540 retval = history(hist, &he, H_NEXT_EVENT, i);
541 if (retval == -1) { 541 if (retval == -1) {
542 /* 542 /*
543 * the notion of first and last is 543 * the notion of first and last is
544 * backwards to that of the history package 544 * backwards to that of the history package
545 */ 545 */
546 retval = history(hist, &he, 546 retval = history(hist, &he,
547 last ? H_FIRST : H_LAST); 547 last ? H_FIRST : H_LAST);
548 } 548 }
549 } 549 }
550 if (retval == -1) 550 if (retval == -1)
551 error("history number %s not found (internal error)", 551 error("history number %s not found (internal error)",
552 str); 552 str);
553 } else { 553 } else {
554 /* 554 /*
555 * pattern 555 * pattern
556 */ 556 */
557 retval = history(hist, &he, H_PREV_STR, str); 557 retval = history(hist, &he, H_PREV_STR, str);
558 if (retval == -1) 558 if (retval == -1)
559 error("history pattern not found: %s", str); 559 error("history pattern not found: %s", str);
560 } 560 }
561 return (he.num); 561 return (he.num);
562} 562}
563#else 563#else
564int 564int
565histcmd(int argc, char **argv) 565histcmd(int argc, char **argv)
566{ 566{
567 error("not compiled with history support"); 567 error("not compiled with history support");
568 /* NOTREACHED */ 568 /* NOTREACHED */
569} 569}
570int 570int
571inputrc(int argc, char **argv) 571inputrc(int argc, char **argv)
572{ 572{
573 error("not compiled with history support"); 573 error("not compiled with history support");
574 /* NOTREACHED */ 574 /* NOTREACHED */
575} 575}
576#endif 576#endif