/bin/sh/histedit.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 501 lines · 361 code · 39 blank · 101 comment · 132 complexity · 1afa2b353122d5235bcaf207e615bd6a MD5 · raw file

  1. /*-
  2. * Copyright (c) 1993
  3. * The Regents of the University of California. All rights reserved.
  4. *
  5. * This code is derived from software contributed to Berkeley by
  6. * Kenneth Almquist.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. * 4. Neither the name of the University nor the names of its contributors
  17. * may be used to endorse or promote products derived from this software
  18. * without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  21. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  23. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  24. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  25. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  26. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  27. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  28. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  29. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  30. * SUCH DAMAGE.
  31. */
  32. #ifndef lint
  33. #if 0
  34. static char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95";
  35. #endif
  36. #endif /* not lint */
  37. #include <sys/cdefs.h>
  38. __FBSDID("$FreeBSD$");
  39. #include <sys/param.h>
  40. #include <limits.h>
  41. #include <paths.h>
  42. #include <stdio.h>
  43. #include <stdlib.h>
  44. #include <unistd.h>
  45. /*
  46. * Editline and history functions (and glue).
  47. */
  48. #include "shell.h"
  49. #include "parser.h"
  50. #include "var.h"
  51. #include "options.h"
  52. #include "main.h"
  53. #include "output.h"
  54. #include "mystring.h"
  55. #ifndef NO_HISTORY
  56. #include "myhistedit.h"
  57. #include "error.h"
  58. #include "eval.h"
  59. #include "memalloc.h"
  60. #include "builtins.h"
  61. #define MAXHISTLOOPS 4 /* max recursions through fc */
  62. #define DEFEDITOR "ed" /* default editor *should* be $EDITOR */
  63. History *hist; /* history cookie */
  64. EditLine *el; /* editline cookie */
  65. int displayhist;
  66. static FILE *el_in, *el_out, *el_err;
  67. static char *fc_replace(const char *, char *, char *);
  68. static int not_fcnumber(const char *);
  69. static int str_to_event(const char *, int);
  70. /*
  71. * Set history and editing status. Called whenever the status may
  72. * have changed (figures out what to do).
  73. */
  74. void
  75. histedit(void)
  76. {
  77. #define editing (Eflag || Vflag)
  78. if (iflag) {
  79. if (!hist) {
  80. /*
  81. * turn history on
  82. */
  83. INTOFF;
  84. hist = history_init();
  85. INTON;
  86. if (hist != NULL)
  87. sethistsize(histsizeval());
  88. else
  89. out2fmt_flush("sh: can't initialize history\n");
  90. }
  91. if (editing && !el && isatty(0)) { /* && isatty(2) ??? */
  92. /*
  93. * turn editing on
  94. */
  95. char *term;
  96. INTOFF;
  97. if (el_in == NULL)
  98. el_in = fdopen(0, "r");
  99. if (el_err == NULL)
  100. el_err = fdopen(1, "w");
  101. if (el_out == NULL)
  102. el_out = fdopen(2, "w");
  103. if (el_in == NULL || el_err == NULL || el_out == NULL)
  104. goto bad;
  105. term = lookupvar("TERM");
  106. if (term)
  107. setenv("TERM", term, 1);
  108. else
  109. unsetenv("TERM");
  110. el = el_init(arg0, el_in, el_out, el_err);
  111. if (el != NULL) {
  112. if (hist)
  113. el_set(el, EL_HIST, history, hist);
  114. el_set(el, EL_PROMPT, getprompt);
  115. el_set(el, EL_ADDFN, "sh-complete",
  116. "Filename completion",
  117. _el_fn_sh_complete);
  118. } else {
  119. bad:
  120. out2fmt_flush("sh: can't initialize editing\n");
  121. }
  122. INTON;
  123. } else if (!editing && el) {
  124. INTOFF;
  125. el_end(el);
  126. el = NULL;
  127. INTON;
  128. }
  129. if (el) {
  130. if (Vflag)
  131. el_set(el, EL_EDITOR, "vi");
  132. else if (Eflag)
  133. el_set(el, EL_EDITOR, "emacs");
  134. el_set(el, EL_BIND, "^I", "sh-complete", NULL);
  135. el_source(el, NULL);
  136. }
  137. } else {
  138. INTOFF;
  139. if (el) { /* no editing if not interactive */
  140. el_end(el);
  141. el = NULL;
  142. }
  143. if (hist) {
  144. history_end(hist);
  145. hist = NULL;
  146. }
  147. INTON;
  148. }
  149. }
  150. void
  151. sethistsize(const char *hs)
  152. {
  153. int histsize;
  154. HistEvent he;
  155. if (hist != NULL) {
  156. if (hs == NULL || *hs == '\0' ||
  157. (histsize = atoi(hs)) < 0)
  158. histsize = 100;
  159. history(hist, &he, H_SETSIZE, histsize);
  160. history(hist, &he, H_SETUNIQUE, 1);
  161. }
  162. }
  163. void
  164. setterm(const char *term)
  165. {
  166. if (rootshell && el != NULL && term != NULL)
  167. el_set(el, EL_TERMINAL, term);
  168. }
  169. int
  170. histcmd(int argc, char **argv __unused)
  171. {
  172. int ch;
  173. const char *editor = NULL;
  174. HistEvent he;
  175. int lflg = 0, nflg = 0, rflg = 0, sflg = 0;
  176. int i, retval;
  177. const char *firststr, *laststr;
  178. int first, last, direction;
  179. char *pat = NULL, *repl = NULL;
  180. static int active = 0;
  181. struct jmploc jmploc;
  182. struct jmploc *savehandler;
  183. char editfilestr[PATH_MAX];
  184. char *volatile editfile;
  185. FILE *efp = NULL;
  186. int oldhistnum;
  187. if (hist == NULL)
  188. error("history not active");
  189. if (argc == 1)
  190. error("missing history argument");
  191. while (not_fcnumber(*argptr) && (ch = nextopt("e:lnrs")) != '\0')
  192. switch ((char)ch) {
  193. case 'e':
  194. editor = shoptarg;
  195. break;
  196. case 'l':
  197. lflg = 1;
  198. break;
  199. case 'n':
  200. nflg = 1;
  201. break;
  202. case 'r':
  203. rflg = 1;
  204. break;
  205. case 's':
  206. sflg = 1;
  207. break;
  208. }
  209. savehandler = handler;
  210. /*
  211. * If executing...
  212. */
  213. if (lflg == 0 || editor || sflg) {
  214. lflg = 0; /* ignore */
  215. editfile = NULL;
  216. /*
  217. * Catch interrupts to reset active counter and
  218. * cleanup temp files.
  219. */
  220. if (setjmp(jmploc.loc)) {
  221. active = 0;
  222. if (editfile)
  223. unlink(editfile);
  224. handler = savehandler;
  225. longjmp(handler->loc, 1);
  226. }
  227. handler = &jmploc;
  228. if (++active > MAXHISTLOOPS) {
  229. active = 0;
  230. displayhist = 0;
  231. error("called recursively too many times");
  232. }
  233. /*
  234. * Set editor.
  235. */
  236. if (sflg == 0) {
  237. if (editor == NULL &&
  238. (editor = bltinlookup("FCEDIT", 1)) == NULL &&
  239. (editor = bltinlookup("EDITOR", 1)) == NULL)
  240. editor = DEFEDITOR;
  241. if (editor[0] == '-' && editor[1] == '\0') {
  242. sflg = 1; /* no edit */
  243. editor = NULL;
  244. }
  245. }
  246. }
  247. /*
  248. * If executing, parse [old=new] now
  249. */
  250. if (lflg == 0 && *argptr != NULL &&
  251. ((repl = strchr(*argptr, '=')) != NULL)) {
  252. pat = *argptr;
  253. *repl++ = '\0';
  254. argptr++;
  255. }
  256. /*
  257. * determine [first] and [last]
  258. */
  259. if (*argptr == NULL) {
  260. firststr = lflg ? "-16" : "-1";
  261. laststr = "-1";
  262. } else if (argptr[1] == NULL) {
  263. firststr = argptr[0];
  264. laststr = lflg ? "-1" : argptr[0];
  265. } else if (argptr[2] == NULL) {
  266. firststr = argptr[0];
  267. laststr = argptr[1];
  268. } else
  269. error("too many arguments");
  270. /*
  271. * Turn into event numbers.
  272. */
  273. first = str_to_event(firststr, 0);
  274. last = str_to_event(laststr, 1);
  275. if (rflg) {
  276. i = last;
  277. last = first;
  278. first = i;
  279. }
  280. /*
  281. * XXX - this should not depend on the event numbers
  282. * always increasing. Add sequence numbers or offset
  283. * to the history element in next (diskbased) release.
  284. */
  285. direction = first < last ? H_PREV : H_NEXT;
  286. /*
  287. * If editing, grab a temp file.
  288. */
  289. if (editor) {
  290. int fd;
  291. INTOFF; /* easier */
  292. sprintf(editfilestr, "%s/_shXXXXXX", _PATH_TMP);
  293. if ((fd = mkstemp(editfilestr)) < 0)
  294. error("can't create temporary file %s", editfile);
  295. editfile = editfilestr;
  296. if ((efp = fdopen(fd, "w")) == NULL) {
  297. close(fd);
  298. error("Out of space");
  299. }
  300. }
  301. /*
  302. * Loop through selected history events. If listing or executing,
  303. * do it now. Otherwise, put into temp file and call the editor
  304. * after.
  305. *
  306. * The history interface needs rethinking, as the following
  307. * convolutions will demonstrate.
  308. */
  309. history(hist, &he, H_FIRST);
  310. retval = history(hist, &he, H_NEXT_EVENT, first);
  311. for (;retval != -1; retval = history(hist, &he, direction)) {
  312. if (lflg) {
  313. if (!nflg)
  314. out1fmt("%5d ", he.num);
  315. out1str(he.str);
  316. } else {
  317. char *s = pat ?
  318. fc_replace(he.str, pat, repl) : (char *)he.str;
  319. if (sflg) {
  320. if (displayhist) {
  321. out2str(s);
  322. flushout(out2);
  323. }
  324. evalstring(s, 0);
  325. if (displayhist && hist) {
  326. /*
  327. * XXX what about recursive and
  328. * relative histnums.
  329. */
  330. oldhistnum = he.num;
  331. history(hist, &he, H_ENTER, s);
  332. /*
  333. * XXX H_ENTER moves the internal
  334. * cursor, set it back to the current
  335. * entry.
  336. */
  337. retval = history(hist, &he,
  338. H_NEXT_EVENT, oldhistnum);
  339. }
  340. } else
  341. fputs(s, efp);
  342. }
  343. /*
  344. * At end? (if we were to lose last, we'd sure be
  345. * messed up).
  346. */
  347. if (he.num == last)
  348. break;
  349. }
  350. if (editor) {
  351. char *editcmd;
  352. fclose(efp);
  353. editcmd = stalloc(strlen(editor) + strlen(editfile) + 2);
  354. sprintf(editcmd, "%s %s", editor, editfile);
  355. evalstring(editcmd, 0); /* XXX - should use no JC command */
  356. INTON;
  357. readcmdfile(editfile); /* XXX - should read back - quick tst */
  358. unlink(editfile);
  359. }
  360. if (lflg == 0 && active > 0)
  361. --active;
  362. if (displayhist)
  363. displayhist = 0;
  364. handler = savehandler;
  365. return 0;
  366. }
  367. static char *
  368. fc_replace(const char *s, char *p, char *r)
  369. {
  370. char *dest;
  371. int plen = strlen(p);
  372. STARTSTACKSTR(dest);
  373. while (*s) {
  374. if (*s == *p && strncmp(s, p, plen) == 0) {
  375. STPUTS(r, dest);
  376. s += plen;
  377. *p = '\0'; /* so no more matches */
  378. } else
  379. STPUTC(*s++, dest);
  380. }
  381. STPUTC('\0', dest);
  382. dest = grabstackstr(dest);
  383. return (dest);
  384. }
  385. static int
  386. not_fcnumber(const char *s)
  387. {
  388. if (s == NULL)
  389. return (0);
  390. if (*s == '-')
  391. s++;
  392. return (!is_number(s));
  393. }
  394. static int
  395. str_to_event(const char *str, int last)
  396. {
  397. HistEvent he;
  398. const char *s = str;
  399. int relative = 0;
  400. int i, retval;
  401. retval = history(hist, &he, H_FIRST);
  402. switch (*s) {
  403. case '-':
  404. relative = 1;
  405. /*FALLTHROUGH*/
  406. case '+':
  407. s++;
  408. }
  409. if (is_number(s)) {
  410. i = atoi(s);
  411. if (relative) {
  412. while (retval != -1 && i--) {
  413. retval = history(hist, &he, H_NEXT);
  414. }
  415. if (retval == -1)
  416. retval = history(hist, &he, H_LAST);
  417. } else {
  418. retval = history(hist, &he, H_NEXT_EVENT, i);
  419. if (retval == -1) {
  420. /*
  421. * the notion of first and last is
  422. * backwards to that of the history package
  423. */
  424. retval = history(hist, &he, last ? H_FIRST : H_LAST);
  425. }
  426. }
  427. if (retval == -1)
  428. error("history number %s not found (internal error)",
  429. str);
  430. } else {
  431. /*
  432. * pattern
  433. */
  434. retval = history(hist, &he, H_PREV_STR, str);
  435. if (retval == -1)
  436. error("history pattern not found: %s", str);
  437. }
  438. return (he.num);
  439. }
  440. int
  441. bindcmd(int argc, char **argv)
  442. {
  443. if (el == NULL)
  444. error("line editing is disabled");
  445. return (el_parse(el, argc, (const char **)argv));
  446. }
  447. #else
  448. #include "error.h"
  449. int
  450. histcmd(int argc, char **argv)
  451. {
  452. error("not compiled with history support");
  453. /*NOTREACHED*/
  454. return (0);
  455. }
  456. int
  457. bindcmd(int argc, char **argv)
  458. {
  459. error("not compiled with line editing support");
  460. return (0);
  461. }
  462. #endif