aboutsummaryrefslogtreecommitdiff
path: root/editors
diff options
context:
space:
mode:
Diffstat (limited to 'editors')
-rw-r--r--editors/awk.c272
-rw-r--r--editors/cmp.c2
-rw-r--r--editors/sed.c2
-rw-r--r--editors/vi.c144
4 files changed, 289 insertions, 131 deletions
diff --git a/editors/awk.c b/editors/awk.c
index 41a57ea0c..9b9b202db 100644
--- a/editors/awk.c
+++ b/editors/awk.c
@@ -66,6 +66,8 @@
66#endif 66#endif
67#ifndef debug_printf_parse 67#ifndef debug_printf_parse
68# define debug_printf_parse(...) (fprintf(stderr, __VA_ARGS__)) 68# define debug_printf_parse(...) (fprintf(stderr, __VA_ARGS__))
69#else
70# define debug_parse_print_tc(...) ((void)0)
69#endif 71#endif
70 72
71 73
@@ -210,13 +212,13 @@ typedef struct tsplitter_s {
210#define TC_SEQTERM (1 << 1) /* ) */ 212#define TC_SEQTERM (1 << 1) /* ) */
211#define TC_REGEXP (1 << 2) /* /.../ */ 213#define TC_REGEXP (1 << 2) /* /.../ */
212#define TC_OUTRDR (1 << 3) /* | > >> */ 214#define TC_OUTRDR (1 << 3) /* | > >> */
213#define TC_UOPPOST (1 << 4) /* unary postfix operator */ 215#define TC_UOPPOST (1 << 4) /* unary postfix operator ++ -- */
214#define TC_UOPPRE1 (1 << 5) /* unary prefix operator */ 216#define TC_UOPPRE1 (1 << 5) /* unary prefix operator ++ -- $ */
215#define TC_BINOPX (1 << 6) /* two-opnd operator */ 217#define TC_BINOPX (1 << 6) /* two-opnd operator */
216#define TC_IN (1 << 7) 218#define TC_IN (1 << 7)
217#define TC_COMMA (1 << 8) 219#define TC_COMMA (1 << 8)
218#define TC_PIPE (1 << 9) /* input redirection pipe */ 220#define TC_PIPE (1 << 9) /* input redirection pipe */
219#define TC_UOPPRE2 (1 << 10) /* unary prefix operator */ 221#define TC_UOPPRE2 (1 << 10) /* unary prefix operator + - ! */
220#define TC_ARRTERM (1 << 11) /* ] */ 222#define TC_ARRTERM (1 << 11) /* ] */
221#define TC_GRPSTART (1 << 12) /* { */ 223#define TC_GRPSTART (1 << 12) /* { */
222#define TC_GRPTERM (1 << 13) /* } */ 224#define TC_GRPTERM (1 << 13) /* } */
@@ -243,14 +245,51 @@ typedef struct tsplitter_s {
243#define TC_STRING (1 << 29) 245#define TC_STRING (1 << 29)
244#define TC_NUMBER (1 << 30) 246#define TC_NUMBER (1 << 30)
245 247
246#define TC_UOPPRE (TC_UOPPRE1 | TC_UOPPRE2) 248#ifndef debug_parse_print_tc
249#define debug_parse_print_tc(n) do { \
250if ((n) & TC_SEQSTART) debug_printf_parse(" SEQSTART"); \
251if ((n) & TC_SEQTERM ) debug_printf_parse(" SEQTERM" ); \
252if ((n) & TC_REGEXP ) debug_printf_parse(" REGEXP" ); \
253if ((n) & TC_OUTRDR ) debug_printf_parse(" OUTRDR" ); \
254if ((n) & TC_UOPPOST ) debug_printf_parse(" UOPPOST" ); \
255if ((n) & TC_UOPPRE1 ) debug_printf_parse(" UOPPRE1" ); \
256if ((n) & TC_BINOPX ) debug_printf_parse(" BINOPX" ); \
257if ((n) & TC_IN ) debug_printf_parse(" IN" ); \
258if ((n) & TC_COMMA ) debug_printf_parse(" COMMA" ); \
259if ((n) & TC_PIPE ) debug_printf_parse(" PIPE" ); \
260if ((n) & TC_UOPPRE2 ) debug_printf_parse(" UOPPRE2" ); \
261if ((n) & TC_ARRTERM ) debug_printf_parse(" ARRTERM" ); \
262if ((n) & TC_GRPSTART) debug_printf_parse(" GRPSTART"); \
263if ((n) & TC_GRPTERM ) debug_printf_parse(" GRPTERM" ); \
264if ((n) & TC_SEMICOL ) debug_printf_parse(" SEMICOL" ); \
265if ((n) & TC_NEWLINE ) debug_printf_parse(" NEWLINE" ); \
266if ((n) & TC_STATX ) debug_printf_parse(" STATX" ); \
267if ((n) & TC_WHILE ) debug_printf_parse(" WHILE" ); \
268if ((n) & TC_ELSE ) debug_printf_parse(" ELSE" ); \
269if ((n) & TC_BUILTIN ) debug_printf_parse(" BUILTIN" ); \
270if ((n) & TC_LENGTH ) debug_printf_parse(" LENGTH" ); \
271if ((n) & TC_GETLINE ) debug_printf_parse(" GETLINE" ); \
272if ((n) & TC_FUNCDECL) debug_printf_parse(" FUNCDECL"); \
273if ((n) & TC_BEGIN ) debug_printf_parse(" BEGIN" ); \
274if ((n) & TC_END ) debug_printf_parse(" END" ); \
275if ((n) & TC_EOF ) debug_printf_parse(" EOF" ); \
276if ((n) & TC_VARIABLE) debug_printf_parse(" VARIABLE"); \
277if ((n) & TC_ARRAY ) debug_printf_parse(" ARRAY" ); \
278if ((n) & TC_FUNCTION) debug_printf_parse(" FUNCTION"); \
279if ((n) & TC_STRING ) debug_printf_parse(" STRING" ); \
280if ((n) & TC_NUMBER ) debug_printf_parse(" NUMBER" ); \
281} while (0)
282#endif
247 283
248/* combined token classes */ 284/* combined token classes */
285#define TC_UOPPRE (TC_UOPPRE1 | TC_UOPPRE2)
286
249#define TC_BINOP (TC_BINOPX | TC_COMMA | TC_PIPE | TC_IN) 287#define TC_BINOP (TC_BINOPX | TC_COMMA | TC_PIPE | TC_IN)
250//#define TC_UNARYOP (TC_UOPPRE | TC_UOPPOST) 288//#define TC_UNARYOP (TC_UOPPRE | TC_UOPPOST)
251#define TC_OPERAND (TC_VARIABLE | TC_ARRAY | TC_FUNCTION \ 289#define TC_OPERAND (TC_VARIABLE | TC_ARRAY | TC_FUNCTION \
252 | TC_BUILTIN | TC_LENGTH | TC_GETLINE \ 290 | TC_BUILTIN | TC_LENGTH | TC_GETLINE \
253 | TC_SEQSTART | TC_STRING | TC_NUMBER) 291 | TC_SEQSTART | TC_STRING | TC_NUMBER)
292#define TC_LVALUE (TC_VARIABLE | TC_ARRAY)
254 293
255#define TC_STATEMNT (TC_STATX | TC_WHILE) 294#define TC_STATEMNT (TC_STATX | TC_WHILE)
256#define TC_OPTERM (TC_SEMICOL | TC_NEWLINE) 295#define TC_OPTERM (TC_SEMICOL | TC_NEWLINE)
@@ -284,7 +323,6 @@ typedef struct tsplitter_s {
284#define OF_CHECKED 0x200000 323#define OF_CHECKED 0x200000
285#define OF_REQUIRED 0x400000 324#define OF_REQUIRED 0x400000
286 325
287
288/* combined operator flags */ 326/* combined operator flags */
289#define xx 0 327#define xx 0
290#define xV OF_RES2 328#define xV OF_RES2
@@ -313,10 +351,8 @@ typedef struct tsplitter_s {
313#define PRIMASK2 0x7E000000 351#define PRIMASK2 0x7E000000
314 352
315/* Operation classes */ 353/* Operation classes */
316
317#define SHIFT_TIL_THIS 0x0600 354#define SHIFT_TIL_THIS 0x0600
318#define RECUR_FROM_THIS 0x1000 355#define RECUR_FROM_THIS 0x1000
319
320enum { 356enum {
321 OC_DELETE = 0x0100, OC_EXEC = 0x0200, OC_NEWSOURCE = 0x0300, 357 OC_DELETE = 0x0100, OC_EXEC = 0x0200, OC_NEWSOURCE = 0x0300,
322 OC_PRINT = 0x0400, OC_PRINTF = 0x0500, OC_WALKINIT = 0x0600, 358 OC_PRINT = 0x0400, OC_PRINTF = 0x0500, OC_WALKINIT = 0x0600,
@@ -411,13 +447,16 @@ static const uint32_t tokeninfo[] ALIGN4 = {
411 OC_REGEXP, 447 OC_REGEXP,
412 xS|'a', xS|'w', xS|'|', 448 xS|'a', xS|'w', xS|'|',
413 OC_UNARY|xV|P(9)|'p', OC_UNARY|xV|P(9)|'m', 449 OC_UNARY|xV|P(9)|'p', OC_UNARY|xV|P(9)|'m',
414 OC_UNARY|xV|P(9)|'P', OC_UNARY|xV|P(9)|'M', OC_FIELD|xV|P(5), 450#define TI_PREINC (OC_UNARY|xV|P(9)|'P')
451#define TI_PREDEC (OC_UNARY|xV|P(9)|'M')
452 TI_PREINC, TI_PREDEC, OC_FIELD|xV|P(5),
415 OC_COMPARE|VV|P(39)|5, OC_MOVE|VV|P(74), OC_REPLACE|NV|P(74)|'+', OC_REPLACE|NV|P(74)|'-', 453 OC_COMPARE|VV|P(39)|5, OC_MOVE|VV|P(74), OC_REPLACE|NV|P(74)|'+', OC_REPLACE|NV|P(74)|'-',
416 OC_REPLACE|NV|P(74)|'*', OC_REPLACE|NV|P(74)|'/', OC_REPLACE|NV|P(74)|'%', OC_REPLACE|NV|P(74)|'&', 454 OC_REPLACE|NV|P(74)|'*', OC_REPLACE|NV|P(74)|'/', OC_REPLACE|NV|P(74)|'%', OC_REPLACE|NV|P(74)|'&',
417 OC_BINARY|NV|P(29)|'+', OC_BINARY|NV|P(29)|'-', OC_REPLACE|NV|P(74)|'&', OC_BINARY|NV|P(15)|'&', 455 OC_BINARY|NV|P(29)|'+', OC_BINARY|NV|P(29)|'-', OC_REPLACE|NV|P(74)|'&', OC_BINARY|NV|P(15)|'&',
418 OC_BINARY|NV|P(25)|'/', OC_BINARY|NV|P(25)|'%', OC_BINARY|NV|P(15)|'&', OC_BINARY|NV|P(25)|'*', 456 OC_BINARY|NV|P(25)|'/', OC_BINARY|NV|P(25)|'%', OC_BINARY|NV|P(15)|'&', OC_BINARY|NV|P(25)|'*',
419 OC_COMPARE|VV|P(39)|4, OC_COMPARE|VV|P(39)|3, OC_COMPARE|VV|P(39)|0, OC_COMPARE|VV|P(39)|1, 457 OC_COMPARE|VV|P(39)|4, OC_COMPARE|VV|P(39)|3, OC_COMPARE|VV|P(39)|0, OC_COMPARE|VV|P(39)|1,
420 OC_COMPARE|VV|P(39)|2, OC_MATCH|Sx|P(45)|'!', OC_MATCH|Sx|P(45)|'~', OC_LAND|Vx|P(55), 458#define TI_LESS (OC_COMPARE|VV|P(39)|2)
459 TI_LESS, OC_MATCH|Sx|P(45)|'!', OC_MATCH|Sx|P(45)|'~', OC_LAND|Vx|P(55),
421 OC_LOR|Vx|P(59), OC_TERNARY|Vx|P(64)|'?', OC_COLON|xx|P(67)|':', 460 OC_LOR|Vx|P(59), OC_TERNARY|Vx|P(64)|'?', OC_COLON|xx|P(67)|':',
422 OC_IN|SV|P(49), /* TC_IN */ 461 OC_IN|SV|P(49), /* TC_IN */
423 OC_COMMA|SS|P(80), 462 OC_COMMA|SS|P(80),
@@ -1074,6 +1113,10 @@ static uint32_t next_token(uint32_t expected)
1074 uint32_t tc; 1113 uint32_t tc;
1075 const uint32_t *ti; 1114 const uint32_t *ti;
1076 1115
1116 debug_printf_parse("%s() expected(%x):", __func__, expected);
1117 debug_parse_print_tc(expected);
1118 debug_printf_parse("\n");
1119
1077 if (t_rollback) { 1120 if (t_rollback) {
1078 debug_printf_parse("%s: using rolled-back token\n", __func__); 1121 debug_printf_parse("%s: using rolled-back token\n", __func__);
1079 t_rollback = FALSE; 1122 t_rollback = FALSE;
@@ -1178,6 +1221,8 @@ static uint32_t next_token(uint32_t expected)
1178 if (!isalnum_(*p)) 1221 if (!isalnum_(*p))
1179 syntax_error(EMSG_UNEXP_TOKEN); /* no */ 1222 syntax_error(EMSG_UNEXP_TOKEN); /* no */
1180 /* yes */ 1223 /* yes */
1224/* "move name one char back" trick: we need a byte for NUL terminator */
1225/* NB: this results in argv[i][-1] being used (!!!) in e.g. "awk -e 'NAME'" case */
1181 t_string = --p; 1226 t_string = --p;
1182 while (isalnum_(*++p)) { 1227 while (isalnum_(*++p)) {
1183 p[-1] = *p; 1228 p[-1] = *p;
@@ -1230,7 +1275,9 @@ static uint32_t next_token(uint32_t expected)
1230 EMSG_UNEXP_EOS : EMSG_UNEXP_TOKEN); 1275 EMSG_UNEXP_EOS : EMSG_UNEXP_TOKEN);
1231 } 1276 }
1232 1277
1233 debug_printf_parse("%s: returning, ltclass:%x t_double:%f\n", __func__, ltclass, t_double); 1278 debug_printf_parse("%s: returning, t_double:%f ltclass:", __func__, t_double);
1279 debug_parse_print_tc(ltclass);
1280 debug_printf_parse("\n");
1234 return ltclass; 1281 return ltclass;
1235#undef concat_inserted 1282#undef concat_inserted
1236#undef save_tclass 1283#undef save_tclass
@@ -1270,7 +1317,7 @@ static node *condition(void)
1270 1317
1271/* parse expression terminated by given argument, return ptr 1318/* parse expression terminated by given argument, return ptr
1272 * to built subtree. Terminator is eaten by parse_expr */ 1319 * to built subtree. Terminator is eaten by parse_expr */
1273static node *parse_expr(uint32_t iexp) 1320static node *parse_expr(uint32_t term_tc)
1274{ 1321{
1275 node sn; 1322 node sn;
1276 node *cn = &sn; 1323 node *cn = &sn;
@@ -1278,15 +1325,17 @@ static node *parse_expr(uint32_t iexp)
1278 uint32_t tc, xtc; 1325 uint32_t tc, xtc;
1279 var *v; 1326 var *v;
1280 1327
1281 debug_printf_parse("%s(%x)\n", __func__, iexp); 1328 debug_printf_parse("%s() term_tc(%x):", __func__, term_tc);
1329 debug_parse_print_tc(term_tc);
1330 debug_printf_parse("\n");
1282 1331
1283 sn.info = PRIMASK; 1332 sn.info = PRIMASK;
1284 sn.r.n = sn.a.n = glptr = NULL; 1333 sn.r.n = sn.a.n = glptr = NULL;
1285 xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP | iexp; 1334 xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP | term_tc;
1286 1335
1287 while (!((tc = next_token(xtc)) & iexp)) { 1336 while (!((tc = next_token(xtc)) & term_tc)) {
1288 1337
1289 if (glptr && (t_info == (OC_COMPARE | VV | P(39) | 2))) { 1338 if (glptr && (t_info == TI_LESS)) {
1290 /* input redirection (<) attached to glptr node */ 1339 /* input redirection (<) attached to glptr node */
1291 debug_printf_parse("%s: input redir\n", __func__); 1340 debug_printf_parse("%s: input redir\n", __func__);
1292 cn = glptr->l.n = new_node(OC_CONCAT | SS | P(37)); 1341 cn = glptr->l.n = new_node(OC_CONCAT | SS | P(37));
@@ -1317,25 +1366,28 @@ static node *parse_expr(uint32_t iexp)
1317 next_token(TC_GETLINE); 1366 next_token(TC_GETLINE);
1318 /* give maximum priority to this pipe */ 1367 /* give maximum priority to this pipe */
1319 cn->info &= ~PRIMASK; 1368 cn->info &= ~PRIMASK;
1320 xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp; 1369 xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | term_tc;
1321 } 1370 }
1322 } else { 1371 } else {
1323 cn->r.n = vn; 1372 cn->r.n = vn;
1324 xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp; 1373 xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | term_tc;
1325 } 1374 }
1326 vn->a.n = cn; 1375 vn->a.n = cn;
1327 1376
1328 } else { 1377 } else {
1329 debug_printf_parse("%s: other\n", __func__); 1378 debug_printf_parse("%s: other, t_info:%x\n", __func__, t_info);
1330 /* for operands and prefix-unary operators, attach them 1379 /* for operands and prefix-unary operators, attach them
1331 * to last node */ 1380 * to last node */
1332 vn = cn; 1381 vn = cn;
1333 cn = vn->r.n = new_node(t_info); 1382 cn = vn->r.n = new_node(t_info);
1334 cn->a.n = vn; 1383 cn->a.n = vn;
1384
1335 xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP; 1385 xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP;
1386 if (t_info == TI_PREINC || t_info == TI_PREDEC)
1387 xtc = TC_LVALUE | TC_UOPPRE1;
1336 if (tc & (TC_OPERAND | TC_REGEXP)) { 1388 if (tc & (TC_OPERAND | TC_REGEXP)) {
1337 debug_printf_parse("%s: TC_OPERAND | TC_REGEXP\n", __func__); 1389 debug_printf_parse("%s: TC_OPERAND | TC_REGEXP\n", __func__);
1338 xtc = TC_UOPPRE | TC_UOPPOST | TC_BINOP | TC_OPERAND | iexp; 1390 xtc = TC_UOPPRE | TC_UOPPOST | TC_BINOP | TC_OPERAND | term_tc;
1339 /* one should be very careful with switch on tclass - 1391 /* one should be very careful with switch on tclass -
1340 * only simple tclasses should be used! */ 1392 * only simple tclasses should be used! */
1341 switch (tc) { 1393 switch (tc) {
@@ -1392,7 +1444,7 @@ static node *parse_expr(uint32_t iexp)
1392 case TC_GETLINE: 1444 case TC_GETLINE:
1393 debug_printf_parse("%s: TC_GETLINE\n", __func__); 1445 debug_printf_parse("%s: TC_GETLINE\n", __func__);
1394 glptr = cn; 1446 glptr = cn;
1395 xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp; 1447 xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | term_tc;
1396 break; 1448 break;
1397 1449
1398 case TC_BUILTIN: 1450 case TC_BUILTIN:
@@ -1607,6 +1659,8 @@ static void parse_program(char *p)
1607 func *f; 1659 func *f;
1608 var *v; 1660 var *v;
1609 1661
1662 debug_printf_parse("%s()\n", __func__);
1663
1610 g_pos = p; 1664 g_pos = p;
1611 t_lineno = 1; 1665 t_lineno = 1;
1612 while ((tclass = next_token(TC_EOF | TC_OPSEQ | TC_GRPSTART | 1666 while ((tclass = next_token(TC_EOF | TC_OPSEQ | TC_GRPSTART |
@@ -1749,12 +1803,22 @@ static char* qrealloc(char *b, int n, int *size)
1749/* resize field storage space */ 1803/* resize field storage space */
1750static void fsrealloc(int size) 1804static void fsrealloc(int size)
1751{ 1805{
1752 int i; 1806 int i, newsize;
1753 1807
1754 if (size >= maxfields) { 1808 if (size >= maxfields) {
1809 /* Sanity cap, easier than catering for overflows */
1810 if (size > 0xffffff)
1811 bb_die_memory_exhausted();
1812
1755 i = maxfields; 1813 i = maxfields;
1756 maxfields = size + 16; 1814 maxfields = size + 16;
1757 Fields = xrealloc(Fields, maxfields * sizeof(Fields[0])); 1815
1816 newsize = maxfields * sizeof(Fields[0]);
1817 debug_printf_eval("fsrealloc: xrealloc(%p, %u)\n", Fields, newsize);
1818 Fields = xrealloc(Fields, newsize);
1819 debug_printf_eval("fsrealloc: Fields=%p..%p\n", Fields, (char*)Fields + newsize - 1);
1820 /* ^^^ did Fields[] move? debug aid for L.v getting "upstaged" by R.v in evaluate() */
1821
1758 for (; i < maxfields; i++) { 1822 for (; i < maxfields; i++) {
1759 Fields[i].type = VF_SPECIAL; 1823 Fields[i].type = VF_SPECIAL;
1760 Fields[i].string = NULL; 1824 Fields[i].string = NULL;
@@ -2633,20 +2697,30 @@ static var *evaluate(node *op, var *res)
2633 /* execute inevitable things */ 2697 /* execute inevitable things */
2634 if (opinfo & OF_RES1) 2698 if (opinfo & OF_RES1)
2635 L.v = evaluate(op1, v1); 2699 L.v = evaluate(op1, v1);
2636 if (opinfo & OF_RES2)
2637 R.v = evaluate(op->r.n, v1+1);
2638 if (opinfo & OF_STR1) { 2700 if (opinfo & OF_STR1) {
2639 L.s = getvar_s(L.v); 2701 L.s = getvar_s(L.v);
2640 debug_printf_eval("L.s:'%s'\n", L.s); 2702 debug_printf_eval("L.s:'%s'\n", L.s);
2641 } 2703 }
2642 if (opinfo & OF_STR2) {
2643 R.s = getvar_s(R.v);
2644 debug_printf_eval("R.s:'%s'\n", R.s);
2645 }
2646 if (opinfo & OF_NUM1) { 2704 if (opinfo & OF_NUM1) {
2647 L_d = getvar_i(L.v); 2705 L_d = getvar_i(L.v);
2648 debug_printf_eval("L_d:%f\n", L_d); 2706 debug_printf_eval("L_d:%f\n", L_d);
2649 } 2707 }
2708 /* NB: Must get string/numeric values of L (done above)
2709 * _before_ evaluate()'ing R.v: if both L and R are $NNNs,
2710 * and right one is large, then L.v points to Fields[NNN1],
2711 * second evaluate() reallocates and moves (!) Fields[],
2712 * R.v points to Fields[NNN2] but L.v now points to freed mem!
2713 * (Seen trying to evaluate "$444 $44444")
2714 */
2715 if (opinfo & OF_RES2) {
2716 R.v = evaluate(op->r.n, v1+1);
2717 //TODO: L.v may be invalid now, set L.v to NULL to catch bugs?
2718 //L.v = NULL;
2719 }
2720 if (opinfo & OF_STR2) {
2721 R.s = getvar_s(R.v);
2722 debug_printf_eval("R.s:'%s'\n", R.s);
2723 }
2650 2724
2651 debug_printf_eval("switch(0x%x)\n", XC(opinfo & OPCLSMASK)); 2725 debug_printf_eval("switch(0x%x)\n", XC(opinfo & OPCLSMASK));
2652 switch (XC(opinfo & OPCLSMASK)) { 2726 switch (XC(opinfo & OPCLSMASK)) {
@@ -2655,6 +2729,7 @@ static var *evaluate(node *op, var *res)
2655 2729
2656 /* test pattern */ 2730 /* test pattern */
2657 case XC( OC_TEST ): 2731 case XC( OC_TEST ):
2732 debug_printf_eval("TEST\n");
2658 if ((op1->info & OPCLSMASK) == OC_COMMA) { 2733 if ((op1->info & OPCLSMASK) == OC_COMMA) {
2659 /* it's range pattern */ 2734 /* it's range pattern */
2660 if ((opinfo & OF_CHECKED) || ptest(op1->l.n)) { 2735 if ((opinfo & OF_CHECKED) || ptest(op1->l.n)) {
@@ -2672,25 +2747,32 @@ static var *evaluate(node *op, var *res)
2672 2747
2673 /* just evaluate an expression, also used as unconditional jump */ 2748 /* just evaluate an expression, also used as unconditional jump */
2674 case XC( OC_EXEC ): 2749 case XC( OC_EXEC ):
2750 debug_printf_eval("EXEC\n");
2675 break; 2751 break;
2676 2752
2677 /* branch, used in if-else and various loops */ 2753 /* branch, used in if-else and various loops */
2678 case XC( OC_BR ): 2754 case XC( OC_BR ):
2755 debug_printf_eval("BR\n");
2679 op = istrue(L.v) ? op->a.n : op->r.n; 2756 op = istrue(L.v) ? op->a.n : op->r.n;
2680 break; 2757 break;
2681 2758
2682 /* initialize for-in loop */ 2759 /* initialize for-in loop */
2683 case XC( OC_WALKINIT ): 2760 case XC( OC_WALKINIT ):
2761 debug_printf_eval("WALKINIT\n");
2684 hashwalk_init(L.v, iamarray(R.v)); 2762 hashwalk_init(L.v, iamarray(R.v));
2685 break; 2763 break;
2686 2764
2687 /* get next array item */ 2765 /* get next array item */
2688 case XC( OC_WALKNEXT ): 2766 case XC( OC_WALKNEXT ):
2767 debug_printf_eval("WALKNEXT\n");
2689 op = hashwalk_next(L.v) ? op->a.n : op->r.n; 2768 op = hashwalk_next(L.v) ? op->a.n : op->r.n;
2690 break; 2769 break;
2691 2770
2692 case XC( OC_PRINT ): 2771 case XC( OC_PRINT ):
2693 case XC( OC_PRINTF ): { 2772 debug_printf_eval("PRINT /\n");
2773 case XC( OC_PRINTF ):
2774 debug_printf_eval("PRINTF\n");
2775 {
2694 FILE *F = stdout; 2776 FILE *F = stdout;
2695 IF_FEATURE_AWK_GNU_EXTENSIONS(int len;) 2777 IF_FEATURE_AWK_GNU_EXTENSIONS(int len;)
2696 2778
@@ -2745,22 +2827,28 @@ static var *evaluate(node *op, var *res)
2745 /* case XC( OC_DELETE ): - moved to happen before arg evaluation */ 2827 /* case XC( OC_DELETE ): - moved to happen before arg evaluation */
2746 2828
2747 case XC( OC_NEWSOURCE ): 2829 case XC( OC_NEWSOURCE ):
2830 debug_printf_eval("NEWSOURCE\n");
2748 g_progname = op->l.new_progname; 2831 g_progname = op->l.new_progname;
2749 break; 2832 break;
2750 2833
2751 case XC( OC_RETURN ): 2834 case XC( OC_RETURN ):
2835 debug_printf_eval("RETURN\n");
2752 copyvar(res, L.v); 2836 copyvar(res, L.v);
2753 break; 2837 break;
2754 2838
2755 case XC( OC_NEXTFILE ): 2839 case XC( OC_NEXTFILE ):
2840 debug_printf_eval("NEXTFILE\n");
2756 nextfile = TRUE; 2841 nextfile = TRUE;
2757 case XC( OC_NEXT ): 2842 case XC( OC_NEXT ):
2843 debug_printf_eval("NEXT\n");
2758 nextrec = TRUE; 2844 nextrec = TRUE;
2759 case XC( OC_DONE ): 2845 case XC( OC_DONE ):
2846 debug_printf_eval("DONE\n");
2760 clrvar(res); 2847 clrvar(res);
2761 break; 2848 break;
2762 2849
2763 case XC( OC_EXIT ): 2850 case XC( OC_EXIT ):
2851 debug_printf_eval("EXIT\n");
2764 awk_exit(L_d); 2852 awk_exit(L_d);
2765 2853
2766 /* -- recursive node type -- */ 2854 /* -- recursive node type -- */
@@ -2780,15 +2868,18 @@ static var *evaluate(node *op, var *res)
2780 break; 2868 break;
2781 2869
2782 case XC( OC_IN ): 2870 case XC( OC_IN ):
2871 debug_printf_eval("IN\n");
2783 setvar_i(res, hash_search(iamarray(R.v), L.s) ? 1 : 0); 2872 setvar_i(res, hash_search(iamarray(R.v), L.s) ? 1 : 0);
2784 break; 2873 break;
2785 2874
2786 case XC( OC_REGEXP ): 2875 case XC( OC_REGEXP ):
2876 debug_printf_eval("REGEXP\n");
2787 op1 = op; 2877 op1 = op;
2788 L.s = getvar_s(intvar[F0]); 2878 L.s = getvar_s(intvar[F0]);
2789 goto re_cont; 2879 goto re_cont;
2790 2880
2791 case XC( OC_MATCH ): 2881 case XC( OC_MATCH ):
2882 debug_printf_eval("MATCH\n");
2792 op1 = op->r.n; 2883 op1 = op->r.n;
2793 re_cont: 2884 re_cont:
2794 { 2885 {
@@ -2814,6 +2905,7 @@ static var *evaluate(node *op, var *res)
2814 break; 2905 break;
2815 2906
2816 case XC( OC_TERNARY ): 2907 case XC( OC_TERNARY ):
2908 debug_printf_eval("TERNARY\n");
2817 if ((op->r.n->info & OPCLSMASK) != OC_COLON) 2909 if ((op->r.n->info & OPCLSMASK) != OC_COLON)
2818 syntax_error(EMSG_POSSIBLE_ERROR); 2910 syntax_error(EMSG_POSSIBLE_ERROR);
2819 res = evaluate(istrue(L.v) ? op->r.n->l.n : op->r.n->r.n, res); 2911 res = evaluate(istrue(L.v) ? op->r.n->l.n : op->r.n->r.n, res);
@@ -2822,6 +2914,7 @@ static var *evaluate(node *op, var *res)
2822 case XC( OC_FUNC ): { 2914 case XC( OC_FUNC ): {
2823 var *vbeg, *v; 2915 var *vbeg, *v;
2824 const char *sv_progname; 2916 const char *sv_progname;
2917 debug_printf_eval("FUNC\n");
2825 2918
2826 /* The body might be empty, still has to eval the args */ 2919 /* The body might be empty, still has to eval the args */
2827 if (!op->r.n->info && !op->r.f->body.first) 2920 if (!op->r.n->info && !op->r.f->body.first)
@@ -2851,7 +2944,10 @@ static var *evaluate(node *op, var *res)
2851 } 2944 }
2852 2945
2853 case XC( OC_GETLINE ): 2946 case XC( OC_GETLINE ):
2854 case XC( OC_PGETLINE ): { 2947 debug_printf_eval("GETLINE /\n");
2948 case XC( OC_PGETLINE ):
2949 debug_printf_eval("PGETLINE\n");
2950 {
2855 rstream *rsm; 2951 rstream *rsm;
2856 int i; 2952 int i;
2857 2953
@@ -2892,6 +2988,7 @@ static var *evaluate(node *op, var *res)
2892 /* simple builtins */ 2988 /* simple builtins */
2893 case XC( OC_FBLTIN ): { 2989 case XC( OC_FBLTIN ): {
2894 double R_d = R_d; /* for compiler */ 2990 double R_d = R_d; /* for compiler */
2991 debug_printf_eval("FBLTIN\n");
2895 2992
2896 switch (opn) { 2993 switch (opn) {
2897 case F_in: 2994 case F_in:
@@ -3005,14 +3102,18 @@ static var *evaluate(node *op, var *res)
3005 } 3102 }
3006 3103
3007 case XC( OC_BUILTIN ): 3104 case XC( OC_BUILTIN ):
3105 debug_printf_eval("BUILTIN\n");
3008 res = exec_builtin(op, res); 3106 res = exec_builtin(op, res);
3009 break; 3107 break;
3010 3108
3011 case XC( OC_SPRINTF ): 3109 case XC( OC_SPRINTF ):
3110 debug_printf_eval("SPRINTF\n");
3012 setvar_p(res, awk_printf(op1, NULL)); 3111 setvar_p(res, awk_printf(op1, NULL));
3013 break; 3112 break;
3014 3113
3015 case XC( OC_UNARY ): { 3114 case XC( OC_UNARY ):
3115 debug_printf_eval("UNARY\n");
3116 {
3016 double Ld, R_d; 3117 double Ld, R_d;
3017 3118
3018 Ld = R_d = getvar_i(R.v); 3119 Ld = R_d = getvar_i(R.v);
@@ -3042,7 +3143,9 @@ static var *evaluate(node *op, var *res)
3042 break; 3143 break;
3043 } 3144 }
3044 3145
3045 case XC( OC_FIELD ): { 3146 case XC( OC_FIELD ):
3147 debug_printf_eval("FIELD\n");
3148 {
3046 int i = (int)getvar_i(R.v); 3149 int i = (int)getvar_i(R.v);
3047 if (i < 0) 3150 if (i < 0)
3048 syntax_error(EMSG_NEGATIVE_FIELD); 3151 syntax_error(EMSG_NEGATIVE_FIELD);
@@ -3059,8 +3162,10 @@ static var *evaluate(node *op, var *res)
3059 3162
3060 /* concatenation (" ") and index joining (",") */ 3163 /* concatenation (" ") and index joining (",") */
3061 case XC( OC_CONCAT ): 3164 case XC( OC_CONCAT ):
3165 debug_printf_eval("CONCAT /\n");
3062 case XC( OC_COMMA ): { 3166 case XC( OC_COMMA ): {
3063 const char *sep = ""; 3167 const char *sep = "";
3168 debug_printf_eval("COMMA\n");
3064 if ((opinfo & OPCLSMASK) == OC_COMMA) 3169 if ((opinfo & OPCLSMASK) == OC_COMMA)
3065 sep = getvar_s(intvar[SUBSEP]); 3170 sep = getvar_s(intvar[SUBSEP]);
3066 setvar_p(res, xasprintf("%s%s%s", L.s, sep, R.s)); 3171 setvar_p(res, xasprintf("%s%s%s", L.s, sep, R.s));
@@ -3068,17 +3173,22 @@ static var *evaluate(node *op, var *res)
3068 } 3173 }
3069 3174
3070 case XC( OC_LAND ): 3175 case XC( OC_LAND ):
3176 debug_printf_eval("LAND\n");
3071 setvar_i(res, istrue(L.v) ? ptest(op->r.n) : 0); 3177 setvar_i(res, istrue(L.v) ? ptest(op->r.n) : 0);
3072 break; 3178 break;
3073 3179
3074 case XC( OC_LOR ): 3180 case XC( OC_LOR ):
3181 debug_printf_eval("LOR\n");
3075 setvar_i(res, istrue(L.v) ? 1 : ptest(op->r.n)); 3182 setvar_i(res, istrue(L.v) ? 1 : ptest(op->r.n));
3076 break; 3183 break;
3077 3184
3078 case XC( OC_BINARY ): 3185 case XC( OC_BINARY ):
3079 case XC( OC_REPLACE ): { 3186 debug_printf_eval("BINARY /\n");
3187 case XC( OC_REPLACE ):
3188 debug_printf_eval("REPLACE\n");
3189 {
3080 double R_d = getvar_i(R.v); 3190 double R_d = getvar_i(R.v);
3081 debug_printf_eval("BINARY/REPLACE: R_d:%f opn:%c\n", R_d, opn); 3191 debug_printf_eval("R_d:%f opn:%c\n", R_d, opn);
3082 switch (opn) { 3192 switch (opn) {
3083 case '+': 3193 case '+':
3084 L_d += R_d; 3194 L_d += R_d;
@@ -3114,6 +3224,7 @@ static var *evaluate(node *op, var *res)
3114 case XC( OC_COMPARE ): { 3224 case XC( OC_COMPARE ): {
3115 int i = i; /* for compiler */ 3225 int i = i; /* for compiler */
3116 double Ld; 3226 double Ld;
3227 debug_printf_eval("COMPARE\n");
3117 3228
3118 if (is_numeric(L.v) && is_numeric(R.v)) { 3229 if (is_numeric(L.v) && is_numeric(R.v)) {
3119 Ld = getvar_i(L.v) - getvar_i(R.v); 3230 Ld = getvar_i(L.v) - getvar_i(R.v);
@@ -3162,20 +3273,19 @@ static var *evaluate(node *op, var *res)
3162 3273
3163static int awk_exit(int r) 3274static int awk_exit(int r)
3164{ 3275{
3165 var tv;
3166 unsigned i; 3276 unsigned i;
3167 hash_item *hi;
3168
3169 zero_out_var(&tv);
3170 3277
3171 if (!exiting) { 3278 if (!exiting) {
3279 var tv;
3172 exiting = TRUE; 3280 exiting = TRUE;
3173 nextrec = FALSE; 3281 nextrec = FALSE;
3282 zero_out_var(&tv);
3174 evaluate(endseq.first, &tv); 3283 evaluate(endseq.first, &tv);
3175 } 3284 }
3176 3285
3177 /* waiting for children */ 3286 /* waiting for children */
3178 for (i = 0; i < fdhash->csize; i++) { 3287 for (i = 0; i < fdhash->csize; i++) {
3288 hash_item *hi;
3179 hi = fdhash->items[i]; 3289 hi = fdhash->items[i];
3180 while (hi) { 3290 while (hi) {
3181 if (hi->data.rs.F && hi->data.rs.is_pipe) 3291 if (hi->data.rs.F && hi->data.rs.is_pipe)
@@ -3255,12 +3365,8 @@ int awk_main(int argc UNUSED_PARAM, char **argv)
3255#if ENABLE_FEATURE_AWK_GNU_EXTENSIONS 3365#if ENABLE_FEATURE_AWK_GNU_EXTENSIONS
3256 llist_t *list_e = NULL; 3366 llist_t *list_e = NULL;
3257#endif 3367#endif
3258 int i, j; 3368 int i;
3259 var *v;
3260 var tv; 3369 var tv;
3261 char **envp;
3262 char *vnames = (char *)vNames; /* cheat */
3263 char *vvalues = (char *)vValues;
3264 3370
3265 INIT_G(); 3371 INIT_G();
3266 3372
@@ -3269,8 +3375,6 @@ int awk_main(int argc UNUSED_PARAM, char **argv)
3269 if (ENABLE_LOCALE_SUPPORT) 3375 if (ENABLE_LOCALE_SUPPORT)
3270 setlocale(LC_NUMERIC, "C"); 3376 setlocale(LC_NUMERIC, "C");
3271 3377
3272 zero_out_var(&tv);
3273
3274 /* allocate global buffer */ 3378 /* allocate global buffer */
3275 g_buf = xmalloc(MAXVARFMT + 1); 3379 g_buf = xmalloc(MAXVARFMT + 1);
3276 3380
@@ -3280,16 +3384,21 @@ int awk_main(int argc UNUSED_PARAM, char **argv)
3280 fnhash = hash_init(); 3384 fnhash = hash_init();
3281 3385
3282 /* initialize variables */ 3386 /* initialize variables */
3283 for (i = 0; *vnames; i++) { 3387 {
3284 intvar[i] = v = newvar(nextword(&vnames)); 3388 char *vnames = (char *)vNames; /* cheat */
3285 if (*vvalues != '\377') 3389 char *vvalues = (char *)vValues;
3286 setvar_s(v, nextword(&vvalues)); 3390 for (i = 0; *vnames; i++) {
3287 else 3391 var *v;
3288 setvar_i(v, 0); 3392 intvar[i] = v = newvar(nextword(&vnames));
3289 3393 if (*vvalues != '\377')
3290 if (*vnames == '*') { 3394 setvar_s(v, nextword(&vvalues));
3291 v->type |= VF_SPECIAL; 3395 else
3292 vnames++; 3396 setvar_i(v, 0);
3397
3398 if (*vnames == '*') {
3399 v->type |= VF_SPECIAL;
3400 vnames++;
3401 }
3293 } 3402 }
3294 } 3403 }
3295 3404
@@ -3301,16 +3410,19 @@ int awk_main(int argc UNUSED_PARAM, char **argv)
3301 newfile("/dev/stderr")->F = stderr; 3410 newfile("/dev/stderr")->F = stderr;
3302 3411
3303 /* Huh, people report that sometimes environ is NULL. Oh well. */ 3412 /* Huh, people report that sometimes environ is NULL. Oh well. */
3304 if (environ) for (envp = environ; *envp; envp++) { 3413 if (environ) {
3305 /* environ is writable, thus we don't strdup it needlessly */ 3414 char **envp;
3306 char *s = *envp; 3415 for (envp = environ; *envp; envp++) {
3307 char *s1 = strchr(s, '='); 3416 /* environ is writable, thus we don't strdup it needlessly */
3308 if (s1) { 3417 char *s = *envp;
3309 *s1 = '\0'; 3418 char *s1 = strchr(s, '=');
3310 /* Both findvar and setvar_u take const char* 3419 if (s1) {
3311 * as 2nd arg -> environment is not trashed */ 3420 *s1 = '\0';
3312 setvar_u(findvar(iamarray(intvar[ENVIRON]), s), s1 + 1); 3421 /* Both findvar and setvar_u take const char*
3313 *s1 = '='; 3422 * as 2nd arg -> environment is not trashed */
3423 setvar_u(findvar(iamarray(intvar[ENVIRON]), s), s1 + 1);
3424 *s1 = '=';
3425 }
3314 } 3426 }
3315 } 3427 }
3316 opt = getopt32(argv, OPTSTR_AWK, &opt_F, &list_v, &list_f, IF_FEATURE_AWK_GNU_EXTENSIONS(&list_e,) NULL); 3428 opt = getopt32(argv, OPTSTR_AWK, &opt_F, &list_v, &list_f, IF_FEATURE_AWK_GNU_EXTENSIONS(&list_e,) NULL);
@@ -3327,30 +3439,43 @@ int awk_main(int argc UNUSED_PARAM, char **argv)
3327 bb_show_usage(); 3439 bb_show_usage();
3328 } 3440 }
3329 while (list_f) { 3441 while (list_f) {
3330 char *s = NULL; 3442 int fd;
3331 FILE *from_file; 3443 char *s;
3332 3444
3333 g_progname = llist_pop(&list_f); 3445 g_progname = llist_pop(&list_f);
3334 from_file = xfopen_stdin(g_progname); 3446 fd = xopen_stdin(g_progname);
3335 /* one byte is reserved for some trick in next_token */ 3447 /* 1st byte is reserved for "move name one char back" trick in next_token */
3336 for (i = j = 1; j > 0; i += j) { 3448 i = 1;
3337 s = xrealloc(s, i + 4096); 3449 s = NULL;
3338 j = fread(s + i, 1, 4094, from_file); 3450 for (;;) {
3451 int sz;
3452 s = xrealloc(s, i + 1000);
3453 sz = safe_read(fd, s + i, 1000);
3454 if (sz <= 0)
3455 break;
3456 i += sz;
3339 } 3457 }
3458 s = xrealloc(s, i + 1); /* trim unused 999 bytes */
3340 s[i] = '\0'; 3459 s[i] = '\0';
3341 fclose(from_file); 3460 close(fd);
3342 parse_program(s + 1); 3461 parse_program(s + 1);
3343 free(s); 3462 free(s);
3344 } 3463 }
3345 g_progname = "cmd. line"; 3464 g_progname = "cmd. line";
3346#if ENABLE_FEATURE_AWK_GNU_EXTENSIONS 3465#if ENABLE_FEATURE_AWK_GNU_EXTENSIONS
3347 while (list_e) { 3466 while (list_e) {
3467 /* NB: "move name one char back" trick in next_token
3468 * can use argv[i][-1] here.
3469 */
3348 parse_program(llist_pop(&list_e)); 3470 parse_program(llist_pop(&list_e));
3349 } 3471 }
3350#endif 3472#endif
3351 if (!(opt & (OPT_f | OPT_e))) { 3473 if (!(opt & (OPT_f | OPT_e))) {
3352 if (!*argv) 3474 if (!*argv)
3353 bb_show_usage(); 3475 bb_show_usage();
3476 /* NB: "move name one char back" trick in next_token
3477 * can use argv[i][-1] here.
3478 */
3354 parse_program(*argv++); 3479 parse_program(*argv++);
3355 } 3480 }
3356 3481
@@ -3361,6 +3486,7 @@ int awk_main(int argc UNUSED_PARAM, char **argv)
3361 setari_u(intvar[ARGV], ++i, *argv++); 3486 setari_u(intvar[ARGV], ++i, *argv++);
3362 setvar_i(intvar[ARGC], i + 1); 3487 setvar_i(intvar[ARGC], i + 1);
3363 3488
3489 zero_out_var(&tv);
3364 evaluate(beginseq.first, &tv); 3490 evaluate(beginseq.first, &tv);
3365 if (!mainseq.first && !endseq.first) 3491 if (!mainseq.first && !endseq.first)
3366 awk_exit(EXIT_SUCCESS); 3492 awk_exit(EXIT_SUCCESS);
diff --git a/editors/cmp.c b/editors/cmp.c
index 6e27a841a..e106d814e 100644
--- a/editors/cmp.c
+++ b/editors/cmp.c
@@ -18,7 +18,7 @@
18//kbuild:lib-$(CONFIG_CMP) += cmp.o 18//kbuild:lib-$(CONFIG_CMP) += cmp.o
19 19
20//usage:#define cmp_trivial_usage 20//usage:#define cmp_trivial_usage
21//usage: "[-l] [-s] FILE1 [FILE2" IF_DESKTOP(" [SKIP1 [SKIP2]]") "]" 21//usage: "[-ls] FILE1 [FILE2" IF_DESKTOP(" [SKIP1 [SKIP2]]") "]"
22//usage:#define cmp_full_usage "\n\n" 22//usage:#define cmp_full_usage "\n\n"
23//usage: "Compare FILE1 with FILE2 (or stdin)\n" 23//usage: "Compare FILE1 with FILE2 (or stdin)\n"
24//usage: "\n -l Write the byte numbers (decimal) and values (octal)" 24//usage: "\n -l Write the byte numbers (decimal) and values (octal)"
diff --git a/editors/sed.c b/editors/sed.c
index b269b58d8..523fb8dba 100644
--- a/editors/sed.c
+++ b/editors/sed.c
@@ -70,7 +70,7 @@
70//usage:#define sed_full_usage "\n\n" 70//usage:#define sed_full_usage "\n\n"
71//usage: " -e CMD Add CMD to sed commands to be executed" 71//usage: " -e CMD Add CMD to sed commands to be executed"
72//usage: "\n -f FILE Add FILE contents to sed commands to be executed" 72//usage: "\n -f FILE Add FILE contents to sed commands to be executed"
73//usage: "\n -i[SFX] Edit files in-place (otherwise sends to stdout)" 73//usage: "\n -i[SFX] Edit files in-place (otherwise write to stdout)"
74//usage: "\n Optionally back files up, appending SFX" 74//usage: "\n Optionally back files up, appending SFX"
75//usage: "\n -n Suppress automatic printing of pattern space" 75//usage: "\n -n Suppress automatic printing of pattern space"
76//usage: "\n -r,-E Use extended regex syntax" 76//usage: "\n -r,-E Use extended regex syntax"
diff --git a/editors/vi.c b/editors/vi.c
index a4d6b21b4..89c567f10 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -181,7 +181,7 @@
181//kbuild:lib-$(CONFIG_VI) += vi.o 181//kbuild:lib-$(CONFIG_VI) += vi.o
182 182
183//usage:#define vi_trivial_usage 183//usage:#define vi_trivial_usage
184//usage: IF_FEATURE_VI_COLON("[-c CMD] ")IF_FEATURE_VI_READONLY("[-R] ")"[FILE]..." 184//usage: IF_FEATURE_VI_COLON("[-c CMD] ")IF_FEATURE_VI_READONLY("[-R] ")"[-H] [FILE]..."
185//usage:#define vi_full_usage "\n\n" 185//usage:#define vi_full_usage "\n\n"
186//usage: "Edit FILE\n" 186//usage: "Edit FILE\n"
187//usage: IF_FEATURE_VI_COLON( 187//usage: IF_FEATURE_VI_COLON(
@@ -191,6 +191,7 @@
191//usage: "\n -R Read-only" 191//usage: "\n -R Read-only"
192//usage: ) 192//usage: )
193//usage: "\n -H List available features" 193//usage: "\n -H List available features"
194// note: non-standard, "vim -H" is Hebrew mode (bidi support)
194 195
195#include "libbb.h" 196#include "libbb.h"
196// Should be after libbb.h: on some systems regex.h needs sys/types.h: 197// Should be after libbb.h: on some systems regex.h needs sys/types.h:
@@ -377,6 +378,7 @@ struct globals {
377#if ENABLE_FEATURE_VI_SETOPTS 378#if ENABLE_FEATURE_VI_SETOPTS
378 int indentcol; // column of recently autoindent, 0 or -1 379 int indentcol; // column of recently autoindent, 0 or -1
379#endif 380#endif
381 smallint cmd_error;
380 382
381 // former statics 383 // former statics
382#if ENABLE_FEATURE_VI_YANKMARK 384#if ENABLE_FEATURE_VI_YANKMARK
@@ -503,6 +505,7 @@ struct globals {
503#define dotcnt (G.dotcnt ) 505#define dotcnt (G.dotcnt )
504#define last_search_pattern (G.last_search_pattern) 506#define last_search_pattern (G.last_search_pattern)
505#define indentcol (G.indentcol ) 507#define indentcol (G.indentcol )
508#define cmd_error (G.cmd_error )
506 509
507#define edit_file__cur_line (G.edit_file__cur_line) 510#define edit_file__cur_line (G.edit_file__cur_line)
508#define refresh__old_offset (G.refresh__old_offset) 511#define refresh__old_offset (G.refresh__old_offset)
@@ -537,6 +540,7 @@ struct globals {
537 last_modified_count = -1; \ 540 last_modified_count = -1; \
538 /* "" but has space for 2 chars: */ \ 541 /* "" but has space for 2 chars: */ \
539 IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \ 542 IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \
543 tabstop = 8; \
540} while (0) 544} while (0)
541 545
542#if ENABLE_FEATURE_VI_CRASHME 546#if ENABLE_FEATURE_VI_CRASHME
@@ -1122,6 +1126,7 @@ static void indicate_error(void)
1122 if (crashme > 0) 1126 if (crashme > 0)
1123 return; 1127 return;
1124#endif 1128#endif
1129 cmd_error = TRUE;
1125 if (!err_method) { 1130 if (!err_method) {
1126 write1(ESC_BELL); 1131 write1(ESC_BELL);
1127 } else { 1132 } else {
@@ -2191,13 +2196,13 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
2191 cmdcnt = 0; 2196 cmdcnt = 0;
2192 end_cmd_q(); // stop adding to q 2197 end_cmd_q(); // stop adding to q
2193 last_status_cksum = 0; // force status update 2198 last_status_cksum = 0; // force status update
2194 if ((p[-1] != '\n') && (dot > text)) { 2199 if ((dot > text) && (p[-1] != '\n')) {
2195 p--; 2200 p--;
2196 } 2201 }
2197#if ENABLE_FEATURE_VI_SETOPTS 2202#if ENABLE_FEATURE_VI_SETOPTS
2198 if (autoindent) { 2203 if (autoindent) {
2199 len = indent_len(bol); 2204 len = indent_len(bol);
2200 if (len && get_column(bol + len) == indentcol) { 2205 if (len && get_column(bol + len) == indentcol && bol[len] == '\n') {
2201 // remove autoindent from otherwise empty line 2206 // remove autoindent from otherwise empty line
2202 text_hole_delete(bol, bol + len - 1, undo); 2207 text_hole_delete(bol, bol + len - 1, undo);
2203 p = bol; 2208 p = bol;
@@ -2437,9 +2442,7 @@ static char *char_search(char *p, const char *pat, int dir_and_range)
2437 struct re_pattern_buffer preg; 2442 struct re_pattern_buffer preg;
2438 const char *err; 2443 const char *err;
2439 char *q; 2444 char *q;
2440 int i; 2445 int i, size, range, start;
2441 int size;
2442 int range;
2443 2446
2444 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED; 2447 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
2445 if (ignorecase) 2448 if (ignorecase)
@@ -2464,31 +2467,26 @@ static char *char_search(char *p, const char *pat, int dir_and_range)
2464 2467
2465 // RANGE could be negative if we are searching backwards 2468 // RANGE could be negative if we are searching backwards
2466 range = q - p; 2469 range = q - p;
2467 q = p;
2468 size = range;
2469 if (range < 0) { 2470 if (range < 0) {
2470 size = -size; 2471 size = -range;
2471 q = p - size; 2472 start = size;
2472 if (q < text) 2473 } else {
2473 q = text; 2474 size = range;
2475 start = 0;
2474 } 2476 }
2477 q = p - start;
2478 if (q < text)
2479 q = text;
2475 // search for the compiled pattern, preg, in p[] 2480 // search for the compiled pattern, preg, in p[]
2476 // range < 0: search backward 2481 // range < 0, start == size: search backward
2477 // range > 0: search forward 2482 // range > 0, start == 0: search forward
2478 // 0 < start < size
2479 // re_search() < 0: not found or error 2483 // re_search() < 0: not found or error
2480 // re_search() >= 0: index of found pattern 2484 // re_search() >= 0: index of found pattern
2481 // struct pattern char int int int struct reg 2485 // struct pattern char int int int struct reg
2482 // re_search(*pattern_buffer, *string, size, start, range, *regs) 2486 // re_search(*pattern_buffer, *string, size, start, range, *regs)
2483 i = re_search(&preg, q, size, /*start:*/ 0, range, /*struct re_registers*:*/ NULL); 2487 i = re_search(&preg, q, size, start, range, /*struct re_registers*:*/ NULL);
2484 regfree(&preg); 2488 regfree(&preg);
2485 if (i < 0) 2489 return i < 0 ? NULL : q + i;
2486 return NULL;
2487 if (dir_and_range > 0) // FORWARD?
2488 p = p + i;
2489 else
2490 p = p - i;
2491 return p;
2492} 2490}
2493# else 2491# else
2494# if ENABLE_FEATURE_VI_SETOPTS 2492# if ENABLE_FEATURE_VI_SETOPTS
@@ -3063,12 +3061,10 @@ static void colon(char *buf)
3063 status_line_bold("No current filename"); 3061 status_line_bold("No current filename");
3064 goto ret; 3062 goto ret;
3065 } 3063 }
3066 if (e < 0) { // no addr given- read after current line 3064 if (e == 0) { // user said ":0r foo"
3067 q = begin_line(dot);
3068 } else if (e == 0) { // user said ":0r foo"
3069 q = text; 3065 q = text;
3070 } else { // addr given- read after that line 3066 } else { // read after given line or current line if none given
3071 q = next_line(find_line(e)); 3067 q = next_line(e > 0 ? find_line(e) : dot);
3072 // read after last line 3068 // read after last line
3073 if (q == end-1) 3069 if (q == end-1)
3074 ++q; 3070 ++q;
@@ -3170,6 +3166,18 @@ static void colon(char *buf)
3170 } 3166 }
3171 len_R = strlen(R); 3167 len_R = strlen(R);
3172 3168
3169 if (len_F) { // save "find" as last search pattern
3170 free(last_search_pattern);
3171 last_search_pattern = xstrdup(F - 1);
3172 last_search_pattern[0] = '/';
3173 } else if (last_search_pattern[1] == '\0') {
3174 status_line_bold("No previous search");
3175 goto ret;
3176 } else {
3177 F = last_search_pattern + 1;
3178 len_F = strlen(F);
3179 }
3180
3173 if (e < 0) { // no addr given 3181 if (e < 0) { // no addr given
3174 q = begin_line(dot); // start with cur line 3182 q = begin_line(dot); // start with cur line
3175 r = end_line(dot); 3183 r = end_line(dot);
@@ -3465,8 +3473,11 @@ static int find_range(char **start, char **stop, int cmd)
3465#endif 3473#endif
3466 // these cmds operate on whole lines 3474 // these cmds operate on whole lines
3467 buftype = WHOLE; 3475 buftype = WHOLE;
3468 if (--cmdcnt > 0) 3476 if (--cmdcnt > 0) {
3469 do_cmd('j'); 3477 do_cmd('j');
3478 if (cmd_error)
3479 buftype = -1;
3480 }
3470 } else if (strchr("^%$0bBeEfFtThnN/?|{}\b\177", c)) { 3481 } else if (strchr("^%$0bBeEfFtThnN/?|{}\b\177", c)) {
3471 // Most operate on char positions within a line. Of those that 3482 // Most operate on char positions within a line. Of those that
3472 // don't '%' needs no special treatment, search commands are 3483 // don't '%' needs no special treatment, search commands are
@@ -3496,6 +3507,8 @@ static int find_range(char **start, char **stop, int cmd)
3496 // these operate on whole lines 3507 // these operate on whole lines
3497 buftype = WHOLE; 3508 buftype = WHOLE;
3498 do_cmd(c); // execute movement cmd 3509 do_cmd(c); // execute movement cmd
3510 if (cmd_error)
3511 buftype = -1;
3499 } else if (c == ' ' || c == 'l') { 3512 } else if (c == ' ' || c == 'l') {
3500 // forward motion by character 3513 // forward motion by character
3501 int tmpcnt = (cmdcnt ?: 1); 3514 int tmpcnt = (cmdcnt ?: 1);
@@ -3581,6 +3594,7 @@ static void do_cmd(int c)
3581// p = q = save_dot = buf; // quiet the compiler 3594// p = q = save_dot = buf; // quiet the compiler
3582 memset(buf, '\0', sizeof(buf)); 3595 memset(buf, '\0', sizeof(buf));
3583 keep_index = FALSE; 3596 keep_index = FALSE;
3597 cmd_error = FALSE;
3584 3598
3585 show_status_line(); 3599 show_status_line();
3586 3600
@@ -3701,24 +3715,30 @@ static void do_cmd(int c)
3701 case 10: // Newline ^J 3715 case 10: // Newline ^J
3702 case 'j': // j- goto next line, same col 3716 case 'j': // j- goto next line, same col
3703 case KEYCODE_DOWN: // cursor key Down 3717 case KEYCODE_DOWN: // cursor key Down
3718 case 13: // Carriage Return ^M
3719 case '+': // +- goto next line
3720 q = dot;
3704 do { 3721 do {
3705 dot_next(); // go to next B-o-l 3722 p = next_line(q);
3723 if (p == end_line(q)) {
3724 indicate_error();
3725 goto dc1;
3726 }
3727 q = p;
3706 } while (--cmdcnt > 0); 3728 } while (--cmdcnt > 0);
3707 // try to stay in saved column 3729 dot = q;
3708 dot = cindex == C_END ? end_line(dot) : move_to_col(dot, cindex); 3730 if (c == 13 || c == '+') {
3709 keep_index = TRUE; 3731 dot_skip_over_ws();
3732 } else {
3733 // try to stay in saved column
3734 dot = cindex == C_END ? end_line(dot) : move_to_col(dot, cindex);
3735 keep_index = TRUE;
3736 }
3710 break; 3737 break;
3711 case 12: // ctrl-L force redraw whole screen 3738 case 12: // ctrl-L force redraw whole screen
3712 case 18: // ctrl-R force redraw 3739 case 18: // ctrl-R force redraw
3713 redraw(TRUE); // this will redraw the entire display 3740 redraw(TRUE); // this will redraw the entire display
3714 break; 3741 break;
3715 case 13: // Carriage Return ^M
3716 case '+': // +- goto next line
3717 do {
3718 dot_next();
3719 } while (--cmdcnt > 0);
3720 dot_skip_over_ws();
3721 break;
3722 case 21: // ctrl-U scroll up half screen 3742 case 21: // ctrl-U scroll up half screen
3723 dot_scroll((rows - 2) / 2, -1); 3743 dot_scroll((rows - 2) / 2, -1);
3724 break; 3744 break;
@@ -3759,6 +3779,8 @@ static void do_cmd(int c)
3759 dot = q; 3779 dot = q;
3760 dot_begin(); // go to B-o-l 3780 dot_begin(); // go to B-o-l
3761 dot_skip_over_ws(); 3781 dot_skip_over_ws();
3782 } else {
3783 indicate_error();
3762 } 3784 }
3763 } else if (c1 == '\'') { // goto previous context 3785 } else if (c1 == '\'') { // goto previous context
3764 dot = swap_context(dot); // swap current and previous context 3786 dot = swap_context(dot); // swap current and previous context
@@ -3884,12 +3906,6 @@ static void do_cmd(int c)
3884 case ',': // ,- repeat latest search in opposite direction 3906 case ',': // ,- repeat latest search in opposite direction
3885 dot_to_char(c != ',' ? last_search_cmd : last_search_cmd ^ 0x20); 3907 dot_to_char(c != ',' ? last_search_cmd : last_search_cmd ^ 0x20);
3886 break; 3908 break;
3887 case '-': // -- goto prev line
3888 do {
3889 dot_prev();
3890 } while (--cmdcnt > 0);
3891 dot_skip_over_ws();
3892 break;
3893#if ENABLE_FEATURE_VI_DOT_CMD 3909#if ENABLE_FEATURE_VI_DOT_CMD
3894 case '.': // .- repeat the last modifying command 3910 case '.': // .- repeat the last modifying command
3895 // Stuff the last_modifying_cmd back into stdin 3911 // Stuff the last_modifying_cmd back into stdin
@@ -4095,9 +4111,10 @@ static void do_cmd(int c)
4095 if (cmdcnt > (rows - 1)) { 4111 if (cmdcnt > (rows - 1)) {
4096 cmdcnt = (rows - 1); 4112 cmdcnt = (rows - 1);
4097 } 4113 }
4098 if (--cmdcnt > 0) { 4114 while (--cmdcnt > 0) {
4099 do_cmd('+'); 4115 dot_next();
4100 } 4116 }
4117 dot_begin();
4101 dot_skip_over_ws(); 4118 dot_skip_over_ws();
4102 break; 4119 break;
4103 case 'I': // I- insert before first non-blank 4120 case 'I': // I- insert before first non-blank
@@ -4134,8 +4151,8 @@ static void do_cmd(int c)
4134 if (cmdcnt > (rows - 1)) { 4151 if (cmdcnt > (rows - 1)) {
4135 cmdcnt = (rows - 1); 4152 cmdcnt = (rows - 1);
4136 } 4153 }
4137 if (--cmdcnt > 0) { 4154 while (--cmdcnt > 0) {
4138 do_cmd('-'); 4155 dot_prev();
4139 } 4156 }
4140 dot_begin(); 4157 dot_begin();
4141 dot_skip_over_ws(); 4158 dot_skip_over_ws();
@@ -4300,12 +4317,24 @@ static void do_cmd(int c)
4300 } 4317 }
4301 case 'k': // k- goto prev line, same col 4318 case 'k': // k- goto prev line, same col
4302 case KEYCODE_UP: // cursor key Up 4319 case KEYCODE_UP: // cursor key Up
4320 case '-': // -- goto prev line
4321 q = dot;
4303 do { 4322 do {
4304 dot_prev(); 4323 p = prev_line(q);
4324 if (p == begin_line(q)) {
4325 indicate_error();
4326 goto dc1;
4327 }
4328 q = p;
4305 } while (--cmdcnt > 0); 4329 } while (--cmdcnt > 0);
4306 // try to stay in saved column 4330 dot = q;
4307 dot = cindex == C_END ? end_line(dot) : move_to_col(dot, cindex); 4331 if (c == '-') {
4308 keep_index = TRUE; 4332 dot_skip_over_ws();
4333 } else {
4334 // try to stay in saved column
4335 dot = cindex == C_END ? end_line(dot) : move_to_col(dot, cindex);
4336 keep_index = TRUE;
4337 }
4309 break; 4338 break;
4310 case 'r': // r- replace the current char with user input 4339 case 'r': // r- replace the current char with user input
4311 c1 = get_one_char(); // get the replacement char 4340 c1 = get_one_char(); // get the replacement char
@@ -4686,7 +4715,6 @@ static void edit_file(char *fn)
4686 4715
4687 cmd_mode = 0; // 0=command 1=insert 2='R'eplace 4716 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
4688 cmdcnt = 0; 4717 cmdcnt = 0;
4689 tabstop = 8;
4690 offset = 0; // no horizontal offset 4718 offset = 0; // no horizontal offset
4691 c = '\0'; 4719 c = '\0';
4692#if ENABLE_FEATURE_VI_DOT_CMD 4720#if ENABLE_FEATURE_VI_DOT_CMD
@@ -4814,7 +4842,11 @@ int vi_main(int argc, char **argv)
4814 initial_cmds[0] = xstrndup(p, MAX_INPUT_LEN); 4842 initial_cmds[0] = xstrndup(p, MAX_INPUT_LEN);
4815 } 4843 }
4816#endif 4844#endif
4817 while ((c = getopt(argc, argv, "hCRH" IF_FEATURE_VI_COLON("c:"))) != -1) { 4845 while ((c = getopt(argc, argv,
4846#if ENABLE_FEATURE_VI_CRASHME
4847 "C"
4848#endif
4849 "RHh" IF_FEATURE_VI_COLON("c:"))) != -1) {
4818 switch (c) { 4850 switch (c) {
4819#if ENABLE_FEATURE_VI_CRASHME 4851#if ENABLE_FEATURE_VI_CRASHME
4820 case 'C': 4852 case 'C':