Deleted Added
full compact
parser.c (213760) parser.c (213811)
1/*-
2 * Copyright (c) 1991, 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

--- 22 unchanged lines hidden (view full) ---

31 */
32
33#ifndef lint
34#if 0
35static char sccsid[] = "@(#)parser.c 8.7 (Berkeley) 5/16/95";
36#endif
37#endif /* not lint */
38#include <sys/cdefs.h>
1/*-
2 * Copyright (c) 1991, 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

--- 22 unchanged lines hidden (view full) ---

31 */
32
33#ifndef lint
34#if 0
35static char sccsid[] = "@(#)parser.c 8.7 (Berkeley) 5/16/95";
36#endif
37#endif /* not lint */
38#include <sys/cdefs.h>
39__FBSDID("$FreeBSD: head/bin/sh/parser.c 213760 2010-10-13 04:01:01Z obrien $");
39__FBSDID("$FreeBSD: head/bin/sh/parser.c 213811 2010-10-13 22:18:03Z obrien $");
40
41#include <stdlib.h>
42#include <unistd.h>
43#include <stdio.h>
44
45#include "shell.h"
46#include "parser.h"
47#include "nodes.h"

--- 52 unchanged lines hidden (view full) ---

100static int startlinno; /* line # where last token started */
101static int funclinno; /* line # where the current function started */
102static struct parser_temp *parser_temp;
103
104/* XXX When 'noaliases' is set to one, no alias expansion takes place. */
105static int noaliases = 0;
106
107
40
41#include <stdlib.h>
42#include <unistd.h>
43#include <stdio.h>
44
45#include "shell.h"
46#include "parser.h"
47#include "nodes.h"

--- 52 unchanged lines hidden (view full) ---

100static int startlinno; /* line # where last token started */
101static int funclinno; /* line # where the current function started */
102static struct parser_temp *parser_temp;
103
104/* XXX When 'noaliases' is set to one, no alias expansion takes place. */
105static int noaliases = 0;
106
107
108STATIC union node *list(int);
109STATIC union node *andor(void);
110STATIC union node *pipeline(void);
111STATIC union node *command(void);
112STATIC union node *simplecmd(union node **, union node *);
113STATIC union node *makename(void);
114STATIC void parsefname(void);
115STATIC void parseheredoc(void);
116STATIC int peektoken(void);
117STATIC int readtoken(void);
118STATIC int xxreadtoken(void);
119STATIC int readtoken1(int, char const *, char *, int);
120STATIC int noexpand(char *);
121STATIC void synexpect(int) __dead2;
122STATIC void synerror(const char *) __dead2;
123STATIC void setprompt(int);
108static union node *list(int);
109static union node *andor(void);
110static union node *pipeline(void);
111static union node *command(void);
112static union node *simplecmd(union node **, union node *);
113static union node *makename(void);
114static void parsefname(void);
115static void parseheredoc(void);
116static int peektoken(void);
117static int readtoken(void);
118static int xxreadtoken(void);
119static int readtoken1(int, char const *, char *, int);
120static int noexpand(char *);
121static void synexpect(int) __dead2;
122static void synerror(const char *) __dead2;
123static void setprompt(int);
124
125
124
125
126STATIC void *
126static void *
127parser_temp_alloc(size_t len)
128{
129 struct parser_temp *t;
130
131 INTOFF;
132 t = ckmalloc(sizeof(*t));
133 t->data = NULL;
134 t->next = parser_temp;
135 parser_temp = t;
136 t->data = ckmalloc(len);
137 INTON;
138 return t->data;
139}
140
141
127parser_temp_alloc(size_t len)
128{
129 struct parser_temp *t;
130
131 INTOFF;
132 t = ckmalloc(sizeof(*t));
133 t->data = NULL;
134 t->next = parser_temp;
135 parser_temp = t;
136 t->data = ckmalloc(len);
137 INTON;
138 return t->data;
139}
140
141
142STATIC void *
142static void *
143parser_temp_realloc(void *ptr, size_t len)
144{
145 struct parser_temp *t;
146
147 INTOFF;
148 t = parser_temp;
149 if (ptr != t->data)
150 error("bug: parser_temp_realloc misused");
151 t->data = ckrealloc(t->data, len);
152 INTON;
153 return t->data;
154}
155
156
143parser_temp_realloc(void *ptr, size_t len)
144{
145 struct parser_temp *t;
146
147 INTOFF;
148 t = parser_temp;
149 if (ptr != t->data)
150 error("bug: parser_temp_realloc misused");
151 t->data = ckrealloc(t->data, len);
152 INTON;
153 return t->data;
154}
155
156
157STATIC void
157static void
158parser_temp_free_upto(void *ptr)
159{
160 struct parser_temp *t;
161 int done = 0;
162
163 INTOFF;
164 while (parser_temp != NULL && !done) {
165 t = parser_temp;
166 parser_temp = t->next;
167 done = t->data == ptr;
168 ckfree(t->data);
169 ckfree(t);
170 }
171 INTON;
172 if (!done)
173 error("bug: parser_temp_free_upto misused");
174}
175
176
158parser_temp_free_upto(void *ptr)
159{
160 struct parser_temp *t;
161 int done = 0;
162
163 INTOFF;
164 while (parser_temp != NULL && !done) {
165 t = parser_temp;
166 parser_temp = t->next;
167 done = t->data == ptr;
168 ckfree(t->data);
169 ckfree(t);
170 }
171 INTON;
172 if (!done)
173 error("bug: parser_temp_free_upto misused");
174}
175
176
177STATIC void
177static void
178parser_temp_free_all(void)
179{
180 struct parser_temp *t;
181
182 INTOFF;
183 while (parser_temp != NULL) {
184 t = parser_temp;
185 parser_temp = t->next;

--- 32 unchanged lines hidden (view full) ---

218 return NEOF;
219 if (t == TNL)
220 return NULL;
221 tokpushback++;
222 return list(1);
223}
224
225
178parser_temp_free_all(void)
179{
180 struct parser_temp *t;
181
182 INTOFF;
183 while (parser_temp != NULL) {
184 t = parser_temp;
185 parser_temp = t->next;

--- 32 unchanged lines hidden (view full) ---

218 return NEOF;
219 if (t == TNL)
220 return NULL;
221 tokpushback++;
222 return list(1);
223}
224
225
226STATIC union node *
226static union node *
227list(int nlflag)
228{
229 union node *n1, *n2, *n3;
230 int tok;
231
232 checkkwd = 2;
233 if (nlflag == 0 && tokendlist[peektoken()])
234 return NULL;

--- 56 unchanged lines hidden (view full) ---

291 tokpushback++;
292 return n1;
293 }
294 }
295}
296
297
298
227list(int nlflag)
228{
229 union node *n1, *n2, *n3;
230 int tok;
231
232 checkkwd = 2;
233 if (nlflag == 0 && tokendlist[peektoken()])
234 return NULL;

--- 56 unchanged lines hidden (view full) ---

291 tokpushback++;
292 return n1;
293 }
294 }
295}
296
297
298
299STATIC union node *
299static union node *
300andor(void)
301{
302 union node *n1, *n2, *n3;
303 int t;
304
305 n1 = pipeline();
306 for (;;) {
307 if ((t = readtoken()) == TAND) {

--- 10 unchanged lines hidden (view full) ---

318 n3->nbinary.ch1 = n1;
319 n3->nbinary.ch2 = n2;
320 n1 = n3;
321 }
322}
323
324
325
300andor(void)
301{
302 union node *n1, *n2, *n3;
303 int t;
304
305 n1 = pipeline();
306 for (;;) {
307 if ((t = readtoken()) == TAND) {

--- 10 unchanged lines hidden (view full) ---

318 n3->nbinary.ch1 = n1;
319 n3->nbinary.ch2 = n2;
320 n1 = n3;
321 }
322}
323
324
325
326STATIC union node *
326static union node *
327pipeline(void)
328{
329 union node *n1, *n2, *pipenode;
330 struct nodelist *lp, *prev;
331 int negate;
332
333 negate = 0;
334 checkkwd = 2;

--- 25 unchanged lines hidden (view full) ---

360 n2->nnot.com = n1;
361 return n2;
362 } else
363 return n1;
364}
365
366
367
327pipeline(void)
328{
329 union node *n1, *n2, *pipenode;
330 struct nodelist *lp, *prev;
331 int negate;
332
333 negate = 0;
334 checkkwd = 2;

--- 25 unchanged lines hidden (view full) ---

360 n2->nnot.com = n1;
361 return n2;
362 } else
363 return n1;
364}
365
366
367
368STATIC union node *
368static union node *
369command(void)
370{
371 union node *n1, *n2;
372 union node *ap, **app;
373 union node *cp, **cpp;
374 union node *redir, **rpp;
375 int t, negate = 0;
376

--- 226 unchanged lines hidden (view full) ---

603 n2->nnot.com = n1;
604 return n2;
605 }
606 else
607 return n1;
608}
609
610
369command(void)
370{
371 union node *n1, *n2;
372 union node *ap, **app;
373 union node *cp, **cpp;
374 union node *redir, **rpp;
375 int t, negate = 0;
376

--- 226 unchanged lines hidden (view full) ---

603 n2->nnot.com = n1;
604 return n2;
605 }
606 else
607 return n1;
608}
609
610
611STATIC union node *
611static union node *
612simplecmd(union node **rpp, union node *redir)
613{
614 union node *args, **app;
615 union node **orig_rpp = rpp;
616 union node *n = NULL;
617
618 /* If we don't have any redirections already, then we must reset */
619 /* rpp to be the address of the local redir variable. */

--- 45 unchanged lines hidden (view full) ---

665 n = (union node *)stalloc(sizeof (struct ncmd));
666 n->type = NCMD;
667 n->ncmd.backgnd = 0;
668 n->ncmd.args = args;
669 n->ncmd.redirect = redir;
670 return n;
671}
672
612simplecmd(union node **rpp, union node *redir)
613{
614 union node *args, **app;
615 union node **orig_rpp = rpp;
616 union node *n = NULL;
617
618 /* If we don't have any redirections already, then we must reset */
619 /* rpp to be the address of the local redir variable. */

--- 45 unchanged lines hidden (view full) ---

665 n = (union node *)stalloc(sizeof (struct ncmd));
666 n->type = NCMD;
667 n->ncmd.backgnd = 0;
668 n->ncmd.args = args;
669 n->ncmd.redirect = redir;
670 return n;
671}
672
673STATIC union node *
673static union node *
674makename(void)
675{
676 union node *n;
677
678 n = (union node *)stalloc(sizeof (struct narg));
679 n->type = NARG;
680 n->narg.next = NULL;
681 n->narg.text = wordtext;

--- 17 unchanged lines hidden (view full) ---

699 if (err)
700 synerror("Bad fd number");
701 else
702 n->ndup.vname = makename();
703 }
704}
705
706
674makename(void)
675{
676 union node *n;
677
678 n = (union node *)stalloc(sizeof (struct narg));
679 n->type = NARG;
680 n->narg.next = NULL;
681 n->narg.text = wordtext;

--- 17 unchanged lines hidden (view full) ---

699 if (err)
700 synerror("Bad fd number");
701 else
702 n->ndup.vname = makename();
703 }
704}
705
706
707STATIC void
707static void
708parsefname(void)
709{
710 union node *n = redirnode;
711
712 if (readtoken() != TWORD)
713 synexpect(-1);
714 if (n->type == NHERE) {
715 struct heredoc *here = heredoc;

--- 25 unchanged lines hidden (view full) ---

741 }
742}
743
744
745/*
746 * Input any here documents.
747 */
748
708parsefname(void)
709{
710 union node *n = redirnode;
711
712 if (readtoken() != TWORD)
713 synexpect(-1);
714 if (n->type == NHERE) {
715 struct heredoc *here = heredoc;

--- 25 unchanged lines hidden (view full) ---

741 }
742}
743
744
745/*
746 * Input any here documents.
747 */
748
749STATIC void
749static void
750parseheredoc(void)
751{
752 struct heredoc *here;
753 union node *n;
754
755 while (heredoclist) {
756 here = heredoclist;
757 heredoclist = here->next;

--- 7 unchanged lines hidden (view full) ---

765 n->narg.type = NARG;
766 n->narg.next = NULL;
767 n->narg.text = wordtext;
768 n->narg.backquote = backquotelist;
769 here->here->nhere.doc = n;
770 }
771}
772
750parseheredoc(void)
751{
752 struct heredoc *here;
753 union node *n;
754
755 while (heredoclist) {
756 here = heredoclist;
757 heredoclist = here->next;

--- 7 unchanged lines hidden (view full) ---

765 n->narg.type = NARG;
766 n->narg.next = NULL;
767 n->narg.text = wordtext;
768 n->narg.backquote = backquotelist;
769 here->here->nhere.doc = n;
770 }
771}
772
773STATIC int
773static int
774peektoken(void)
775{
776 int t;
777
778 t = readtoken();
779 tokpushback++;
780 return (t);
781}
782
774peektoken(void)
775{
776 int t;
777
778 t = readtoken();
779 tokpushback++;
780 return (t);
781}
782
783STATIC int
783static int
784readtoken(void)
785{
786 int t;
787 int savecheckkwd = checkkwd;
788 struct alias *ap;
789#ifdef DEBUG
790 int alreadyseen = tokpushback;
791#endif

--- 63 unchanged lines hidden (view full) ---

855 * word parsing code into a separate routine. In this case, readtoken
856 * doesn't need to have any internal procedures, but parseword does.
857 * We could also make parseoperator in essence the main routine, and
858 * have parseword (readtoken1?) handle both words and redirection.]
859 */
860
861#define RETURN(token) return lasttoken = token
862
784readtoken(void)
785{
786 int t;
787 int savecheckkwd = checkkwd;
788 struct alias *ap;
789#ifdef DEBUG
790 int alreadyseen = tokpushback;
791#endif

--- 63 unchanged lines hidden (view full) ---

855 * word parsing code into a separate routine. In this case, readtoken
856 * doesn't need to have any internal procedures, but parseword does.
857 * We could also make parseoperator in essence the main routine, and
858 * have parseword (readtoken1?) handle both words and redirection.]
859 */
860
861#define RETURN(token) return lasttoken = token
862
863STATIC int
863static int
864xxreadtoken(void)
865{
866 int c;
867
868 if (tokpushback) {
869 tokpushback = 0;
870 return lasttoken;
871 }

--- 54 unchanged lines hidden (view full) ---

926 }
927 }
928breakloop:
929 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
930#undef RETURN
931}
932
933
864xxreadtoken(void)
865{
866 int c;
867
868 if (tokpushback) {
869 tokpushback = 0;
870 return lasttoken;
871 }

--- 54 unchanged lines hidden (view full) ---

926 }
927 }
928breakloop:
929 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
930#undef RETURN
931}
932
933
934#define MAXNEST_STATIC 8
934#define MAXNEST_static 8
935struct tokenstate
936{
937 const char *syntax; /* *SYNTAX */
938 int parenlevel; /* levels of parentheses in arithmetic */
939 enum tokenstate_category
940 {
941 TSTATE_TOP,
942 TSTATE_VAR_OLD, /* ${var+-=?}, inherits dquotes */
943 TSTATE_VAR_NEW, /* other ${var...}, own dquote state */
944 TSTATE_ARITH
945 } category;
946};
947
948
949/*
950 * Called to parse command substitutions.
951 */
952
935struct tokenstate
936{
937 const char *syntax; /* *SYNTAX */
938 int parenlevel; /* levels of parentheses in arithmetic */
939 enum tokenstate_category
940 {
941 TSTATE_TOP,
942 TSTATE_VAR_OLD, /* ${var+-=?}, inherits dquotes */
943 TSTATE_VAR_NEW, /* other ${var...}, own dquote state */
944 TSTATE_ARITH
945 } category;
946};
947
948
949/*
950 * Called to parse command substitutions.
951 */
952
953STATIC char *
953static char *
954parsebackq(char *out, struct nodelist **pbqlist,
955 int oldstyle, int dblquote, int quoted)
956{
957 struct nodelist **nlpp;
958 union node *n;
959 char *volatile str;
960 struct jmploc jmploc;
961 struct jmploc *const savehandler = handler;

--- 164 unchanged lines hidden (view full) ---

1126 * will run code that appears at the end of readtoken1.
1127 */
1128
1129#define CHECKEND() {goto checkend; checkend_return:;}
1130#define PARSEREDIR() {goto parseredir; parseredir_return:;}
1131#define PARSESUB() {goto parsesub; parsesub_return:;}
1132#define PARSEARITH() {goto parsearith; parsearith_return:;}
1133
954parsebackq(char *out, struct nodelist **pbqlist,
955 int oldstyle, int dblquote, int quoted)
956{
957 struct nodelist **nlpp;
958 union node *n;
959 char *volatile str;
960 struct jmploc jmploc;
961 struct jmploc *const savehandler = handler;

--- 164 unchanged lines hidden (view full) ---

1126 * will run code that appears at the end of readtoken1.
1127 */
1128
1129#define CHECKEND() {goto checkend; checkend_return:;}
1130#define PARSEREDIR() {goto parseredir; parseredir_return:;}
1131#define PARSESUB() {goto parsesub; parsesub_return:;}
1132#define PARSEARITH() {goto parsearith; parsearith_return:;}
1133
1134STATIC int
1134static int
1135readtoken1(int firstc, char const *initialsyntax, char *eofmark, int striptabs)
1136{
1137 int c = firstc;
1138 char *out;
1139 int len;
1140 char line[EOFMARKLEN + 1];
1141 struct nodelist *bqlist;
1142 int quotef;
1143 int newvarnest;
1144 int level;
1145 int synentry;
1135readtoken1(int firstc, char const *initialsyntax, char *eofmark, int striptabs)
1136{
1137 int c = firstc;
1138 char *out;
1139 int len;
1140 char line[EOFMARKLEN + 1];
1141 struct nodelist *bqlist;
1142 int quotef;
1143 int newvarnest;
1144 int level;
1145 int synentry;
1146 struct tokenstate state_static[MAXNEST_STATIC];
1147 int maxnest = MAXNEST_STATIC;
1146 struct tokenstate state_static[MAXNEST_static];
1147 int maxnest = MAXNEST_static;
1148 struct tokenstate *state = state_static;
1149
1150 startlinno = plinno;
1151 quotef = 0;
1152 bqlist = NULL;
1153 newvarnest = 0;
1154 level = 0;
1155 state[level].syntax = initialsyntax;

--- 393 unchanged lines hidden (view full) ---

1549 *(stackblock() + typeloc) = subtype | flags;
1550 if (subtype != VSNORMAL) {
1551 if (level + 1 >= maxnest) {
1552 maxnest *= 2;
1553 if (state == state_static) {
1554 state = parser_temp_alloc(
1555 maxnest * sizeof(*state));
1556 memcpy(state, state_static,
1148 struct tokenstate *state = state_static;
1149
1150 startlinno = plinno;
1151 quotef = 0;
1152 bqlist = NULL;
1153 newvarnest = 0;
1154 level = 0;
1155 state[level].syntax = initialsyntax;

--- 393 unchanged lines hidden (view full) ---

1549 *(stackblock() + typeloc) = subtype | flags;
1550 if (subtype != VSNORMAL) {
1551 if (level + 1 >= maxnest) {
1552 maxnest *= 2;
1553 if (state == state_static) {
1554 state = parser_temp_alloc(
1555 maxnest * sizeof(*state));
1556 memcpy(state, state_static,
1557 MAXNEST_STATIC * sizeof(*state));
1557 MAXNEST_static * sizeof(*state));
1558 } else
1559 state = parser_temp_realloc(state,
1560 maxnest * sizeof(*state));
1561 }
1562 level++;
1563 state[level].parenlevel = 0;
1564 if (subtype == VSMINUS || subtype == VSPLUS ||
1565 subtype == VSQUESTION || subtype == VSASSIGN) {

--- 26 unchanged lines hidden (view full) ---

1592parsearith: {
1593
1594 if (level + 1 >= maxnest) {
1595 maxnest *= 2;
1596 if (state == state_static) {
1597 state = parser_temp_alloc(
1598 maxnest * sizeof(*state));
1599 memcpy(state, state_static,
1558 } else
1559 state = parser_temp_realloc(state,
1560 maxnest * sizeof(*state));
1561 }
1562 level++;
1563 state[level].parenlevel = 0;
1564 if (subtype == VSMINUS || subtype == VSPLUS ||
1565 subtype == VSQUESTION || subtype == VSASSIGN) {

--- 26 unchanged lines hidden (view full) ---

1592parsearith: {
1593
1594 if (level + 1 >= maxnest) {
1595 maxnest *= 2;
1596 if (state == state_static) {
1597 state = parser_temp_alloc(
1598 maxnest * sizeof(*state));
1599 memcpy(state, state_static,
1600 MAXNEST_STATIC * sizeof(*state));
1600 MAXNEST_static * sizeof(*state));
1601 } else
1602 state = parser_temp_realloc(state,
1603 maxnest * sizeof(*state));
1604 }
1605 level++;
1606 state[level].syntax = ARISYNTAX;
1607 state[level].parenlevel = 0;
1608 state[level].category = TSTATE_ARITH;

--- 16 unchanged lines hidden (view full) ---

1625}
1626#endif
1627
1628/*
1629 * Returns true if the text contains nothing to expand (no dollar signs
1630 * or backquotes).
1631 */
1632
1601 } else
1602 state = parser_temp_realloc(state,
1603 maxnest * sizeof(*state));
1604 }
1605 level++;
1606 state[level].syntax = ARISYNTAX;
1607 state[level].parenlevel = 0;
1608 state[level].category = TSTATE_ARITH;

--- 16 unchanged lines hidden (view full) ---

1625}
1626#endif
1627
1628/*
1629 * Returns true if the text contains nothing to expand (no dollar signs
1630 * or backquotes).
1631 */
1632
1633STATIC int
1633static int
1634noexpand(char *text)
1635{
1636 char *p;
1637 char c;
1638
1639 p = text;
1640 while ((c = *p++) != '\0') {
1641 if ( c == CTLQUOTEMARK)

--- 29 unchanged lines hidden (view full) ---

1671
1672
1673/*
1674 * Called when an unexpected token is read during the parse. The argument
1675 * is the token that is expected, or -1 if more than one type of token can
1676 * occur at this point.
1677 */
1678
1634noexpand(char *text)
1635{
1636 char *p;
1637 char c;
1638
1639 p = text;
1640 while ((c = *p++) != '\0') {
1641 if ( c == CTLQUOTEMARK)

--- 29 unchanged lines hidden (view full) ---

1671
1672
1673/*
1674 * Called when an unexpected token is read during the parse. The argument
1675 * is the token that is expected, or -1 if more than one type of token can
1676 * occur at this point.
1677 */
1678
1679STATIC void
1679static void
1680synexpect(int token)
1681{
1682 char msg[64];
1683
1684 if (token >= 0) {
1685 fmtstr(msg, 64, "%s unexpected (expecting %s)",
1686 tokname[lasttoken], tokname[token]);
1687 } else {
1688 fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
1689 }
1690 synerror(msg);
1691}
1692
1693
1680synexpect(int token)
1681{
1682 char msg[64];
1683
1684 if (token >= 0) {
1685 fmtstr(msg, 64, "%s unexpected (expecting %s)",
1686 tokname[lasttoken], tokname[token]);
1687 } else {
1688 fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
1689 }
1690 synerror(msg);
1691}
1692
1693
1694STATIC void
1694static void
1695synerror(const char *msg)
1696{
1697 if (commandname)
1698 outfmt(out2, "%s: %d: ", commandname, startlinno);
1699 outfmt(out2, "Syntax error: %s\n", msg);
1700 error((char *)NULL);
1701}
1702
1695synerror(const char *msg)
1696{
1697 if (commandname)
1698 outfmt(out2, "%s: %d: ", commandname, startlinno);
1699 outfmt(out2, "Syntax error: %s\n", msg);
1700 error((char *)NULL);
1701}
1702
1703STATIC void
1703static void
1704setprompt(int which)
1705{
1706 whichprompt = which;
1707
1708#ifndef NO_HISTORY
1709 if (!el)
1710#endif
1711 {

--- 109 unchanged lines hidden ---
1704setprompt(int which)
1705{
1706 whichprompt = which;
1707
1708#ifndef NO_HISTORY
1709 if (!el)
1710#endif
1711 {

--- 109 unchanged lines hidden ---