diff options
Diffstat (limited to 'editors')
-rw-r--r-- | editors/awk.c | 272 | ||||
-rw-r--r-- | editors/cmp.c | 2 | ||||
-rw-r--r-- | editors/sed.c | 2 | ||||
-rw-r--r-- | editors/vi.c | 144 |
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 { \ | ||
250 | if ((n) & TC_SEQSTART) debug_printf_parse(" SEQSTART"); \ | ||
251 | if ((n) & TC_SEQTERM ) debug_printf_parse(" SEQTERM" ); \ | ||
252 | if ((n) & TC_REGEXP ) debug_printf_parse(" REGEXP" ); \ | ||
253 | if ((n) & TC_OUTRDR ) debug_printf_parse(" OUTRDR" ); \ | ||
254 | if ((n) & TC_UOPPOST ) debug_printf_parse(" UOPPOST" ); \ | ||
255 | if ((n) & TC_UOPPRE1 ) debug_printf_parse(" UOPPRE1" ); \ | ||
256 | if ((n) & TC_BINOPX ) debug_printf_parse(" BINOPX" ); \ | ||
257 | if ((n) & TC_IN ) debug_printf_parse(" IN" ); \ | ||
258 | if ((n) & TC_COMMA ) debug_printf_parse(" COMMA" ); \ | ||
259 | if ((n) & TC_PIPE ) debug_printf_parse(" PIPE" ); \ | ||
260 | if ((n) & TC_UOPPRE2 ) debug_printf_parse(" UOPPRE2" ); \ | ||
261 | if ((n) & TC_ARRTERM ) debug_printf_parse(" ARRTERM" ); \ | ||
262 | if ((n) & TC_GRPSTART) debug_printf_parse(" GRPSTART"); \ | ||
263 | if ((n) & TC_GRPTERM ) debug_printf_parse(" GRPTERM" ); \ | ||
264 | if ((n) & TC_SEMICOL ) debug_printf_parse(" SEMICOL" ); \ | ||
265 | if ((n) & TC_NEWLINE ) debug_printf_parse(" NEWLINE" ); \ | ||
266 | if ((n) & TC_STATX ) debug_printf_parse(" STATX" ); \ | ||
267 | if ((n) & TC_WHILE ) debug_printf_parse(" WHILE" ); \ | ||
268 | if ((n) & TC_ELSE ) debug_printf_parse(" ELSE" ); \ | ||
269 | if ((n) & TC_BUILTIN ) debug_printf_parse(" BUILTIN" ); \ | ||
270 | if ((n) & TC_LENGTH ) debug_printf_parse(" LENGTH" ); \ | ||
271 | if ((n) & TC_GETLINE ) debug_printf_parse(" GETLINE" ); \ | ||
272 | if ((n) & TC_FUNCDECL) debug_printf_parse(" FUNCDECL"); \ | ||
273 | if ((n) & TC_BEGIN ) debug_printf_parse(" BEGIN" ); \ | ||
274 | if ((n) & TC_END ) debug_printf_parse(" END" ); \ | ||
275 | if ((n) & TC_EOF ) debug_printf_parse(" EOF" ); \ | ||
276 | if ((n) & TC_VARIABLE) debug_printf_parse(" VARIABLE"); \ | ||
277 | if ((n) & TC_ARRAY ) debug_printf_parse(" ARRAY" ); \ | ||
278 | if ((n) & TC_FUNCTION) debug_printf_parse(" FUNCTION"); \ | ||
279 | if ((n) & TC_STRING ) debug_printf_parse(" STRING" ); \ | ||
280 | if ((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 | |||
320 | enum { | 356 | enum { |
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 */ |
1273 | static node *parse_expr(uint32_t iexp) | 1320 | static 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 */ |
1750 | static void fsrealloc(int size) | 1804 | static 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 | ||
3163 | static int awk_exit(int r) | 3274 | static 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': |