Changeset 3621 in MondoRescue for branches/3.3/mindi-busybox/editors
- Timestamp:
- Dec 20, 2016, 4:07:32 PM (8 years ago)
- Location:
- branches/3.3
- Files:
-
- 10 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
branches/3.3/mindi-busybox/editors/Config.src
r3232 r3621 7 7 8 8 INSERT 9 10 config AWK11 bool "awk"12 default y13 help14 Awk is used as a pattern scanning and processing language. This is15 the BusyBox implementation of that programming language.16 17 config FEATURE_AWK_LIBM18 bool "Enable math functions (requires libm)"19 default y20 depends on AWK21 help22 Enable math functions of the Awk programming language.23 NOTE: This will require libm to be present for linking.24 25 config CMP26 bool "cmp"27 default y28 help29 cmp is used to compare two files and returns the result30 to standard output.31 32 config DIFF33 bool "diff"34 default y35 help36 diff compares two files or directories and outputs the37 differences between them in a form that can be given to38 the patch command.39 40 config FEATURE_DIFF_LONG_OPTIONS41 bool "Enable long options"42 default y43 depends on DIFF && LONG_OPTS44 help45 Enable use of long options.46 47 config FEATURE_DIFF_DIR48 bool "Enable directory support"49 default y50 depends on DIFF51 help52 This option enables support for directory and subdirectory53 comparison.54 55 config ED56 bool "ed"57 default y58 help59 The original 1970's Unix text editor, from the days of teletypes.60 Small, simple, evil. Part of SUSv3. If you're not already using61 this, you don't need it.62 63 config SED64 bool "sed"65 default y66 help67 sed is used to perform text transformations on a file68 or input from a pipeline.69 9 70 10 config FEATURE_ALLOW_EXEC -
branches/3.3/mindi-busybox/editors/Kbuild.src
r3232 r3621 8 8 9 9 INSERT 10 lib-$(CONFIG_AWK) += awk.o11 lib-$(CONFIG_CMP) += cmp.o12 lib-$(CONFIG_DIFF) += diff.o13 lib-$(CONFIG_ED) += ed.o14 lib-$(CONFIG_SED) += sed.o -
branches/3.3/mindi-busybox/editors/awk.c
r3232 r3621 8 8 */ 9 9 10 //config:config AWK 11 //config: bool "awk" 12 //config: default y 13 //config: help 14 //config: Awk is used as a pattern scanning and processing language. This is 15 //config: the BusyBox implementation of that programming language. 16 //config: 17 //config:config FEATURE_AWK_LIBM 18 //config: bool "Enable math functions (requires libm)" 19 //config: default y 20 //config: depends on AWK 21 //config: help 22 //config: Enable math functions of the Awk programming language. 23 //config: NOTE: This will require libm to be present for linking. 24 //config: 25 //config:config FEATURE_AWK_GNU_EXTENSIONS 26 //config: bool "Enable a few GNU extensions" 27 //config: default y 28 //config: depends on AWK 29 //config: help 30 //config: Enable a few features from gawk: 31 //config: * command line option -e AWK_PROGRAM 32 //config: * simultaneous use of -f and -e on the command line. 33 //config: This enables the use of awk library files. 34 //config: Ex: awk -f mylib.awk -e '{print myfunction($1);}' ... 35 36 //applet:IF_AWK(APPLET_NOEXEC(awk, awk, BB_DIR_USR_BIN, BB_SUID_DROP, awk)) 37 38 //kbuild:lib-$(CONFIG_AWK) += awk.o 39 10 40 //usage:#define awk_trivial_usage 11 41 //usage: "[OPTIONS] [AWK_PROGRAM] [FILE]..." … … 14 44 //usage: "\n -F SEP Use SEP as field separator" 15 45 //usage: "\n -f FILE Read program from FILE" 46 //usage: IF_FEATURE_AWK_GNU_EXTENSIONS( 47 //usage: "\n -e AWK_PROGRAM" 48 //usage: ) 16 49 17 50 #include "libbb.h" … … 39 72 40 73 74 #define OPTSTR_AWK \ 75 "F:v:f:" \ 76 IF_FEATURE_AWK_GNU_EXTENSIONS("e:") \ 77 "W:" 78 #define OPTCOMPLSTR_AWK \ 79 "v::f::" \ 80 IF_FEATURE_AWK_GNU_EXTENSIONS("e::") 81 enum { 82 OPTBIT_F, /* define field separator */ 83 OPTBIT_v, /* define variable */ 84 OPTBIT_f, /* pull in awk program from file */ 85 IF_FEATURE_AWK_GNU_EXTENSIONS(OPTBIT_e,) /* -e AWK_PROGRAM */ 86 OPTBIT_W, /* -W ignored */ 87 OPT_F = 1 << OPTBIT_F, 88 OPT_v = 1 << OPTBIT_v, 89 OPT_f = 1 << OPTBIT_f, 90 OPT_e = IF_FEATURE_AWK_GNU_EXTENSIONS((1 << OPTBIT_e)) + 0, 91 OPT_W = 1 << OPTBIT_W 92 }; 41 93 42 94 #define MAXVARFMT 240 … … 156 208 /* simple token classes */ 157 209 /* Order and hex values are very important!!! See next_token() */ 158 #define TC_SEQSTART 1/* ( */210 #define TC_SEQSTART (1 << 0) /* ( */ 159 211 #define TC_SEQTERM (1 << 1) /* ) */ 160 212 #define TC_REGEXP (1 << 2) /* /.../ */ … … 176 228 #define TC_ELSE (1 << 18) 177 229 #define TC_BUILTIN (1 << 19) 178 #define TC_GETLINE (1 << 20) 179 #define TC_FUNCDECL (1 << 21) /* `function' `func' */ 180 #define TC_BEGIN (1 << 22) 181 #define TC_END (1 << 23) 182 #define TC_EOF (1 << 24) 183 #define TC_VARIABLE (1 << 25) 184 #define TC_ARRAY (1 << 26) 185 #define TC_FUNCTION (1 << 27) 186 #define TC_STRING (1 << 28) 187 #define TC_NUMBER (1 << 29) 230 /* This costs ~50 bytes of code. 231 * A separate class to support deprecated "length" form. If we don't need that 232 * (i.e. if we demand that only "length()" with () is valid), then TC_LENGTH 233 * can be merged with TC_BUILTIN: 234 */ 235 #define TC_LENGTH (1 << 20) 236 #define TC_GETLINE (1 << 21) 237 #define TC_FUNCDECL (1 << 22) /* `function' `func' */ 238 #define TC_BEGIN (1 << 23) 239 #define TC_END (1 << 24) 240 #define TC_EOF (1 << 25) 241 #define TC_VARIABLE (1 << 26) 242 #define TC_ARRAY (1 << 27) 243 #define TC_FUNCTION (1 << 28) 244 #define TC_STRING (1 << 29) 245 #define TC_NUMBER (1 << 30) 188 246 189 247 #define TC_UOPPRE (TC_UOPPRE1 | TC_UOPPRE2) … … 191 249 /* combined token classes */ 192 250 #define TC_BINOP (TC_BINOPX | TC_COMMA | TC_PIPE | TC_IN) 193 #define TC_UNARYOP (TC_UOPPRE | TC_UOPPOST)251 //#define TC_UNARYOP (TC_UOPPRE | TC_UOPPOST) 194 252 #define TC_OPERAND (TC_VARIABLE | TC_ARRAY | TC_FUNCTION \ 195 | TC_BUILTIN | TC_GETLINE | TC_SEQSTART | TC_STRING | TC_NUMBER) 253 | TC_BUILTIN | TC_LENGTH | TC_GETLINE \ 254 | TC_SEQSTART | TC_STRING | TC_NUMBER) 196 255 197 256 #define TC_STATEMNT (TC_STATX | TC_WHILE) … … 199 258 200 259 /* word tokens, cannot mean something else if not expected */ 201 #define TC_WORD (TC_IN | TC_STATEMNT | TC_ELSE | TC_BUILTIN \ 202 | TC_GETLINE | TC_FUNCDECL | TC_BEGIN | TC_END) 260 #define TC_WORD (TC_IN | TC_STATEMNT | TC_ELSE \ 261 | TC_BUILTIN | TC_LENGTH | TC_GETLINE \ 262 | TC_FUNCDECL | TC_BEGIN | TC_END) 203 263 204 264 /* discard newlines after these */ … … 295 355 #define NTCC '\377' 296 356 297 #define OC_B OC_BUILTIN298 299 357 static const char tokenlist[] ALIGN1 = 300 "\1(" NTC 301 "\1)" NTC 302 "\1/" NTC /* REGEXP */303 "\2>>" "\1>" "\1|" NTC /* OUTRDR */304 "\2++" "\2--" NTC /* UOPPOST */305 "\2++" "\2--" "\1$" NTC /* UOPPRE1 */306 "\2==" "\1=" "\2+=" "\2-=" /* BINOPX */358 "\1(" NTC /* TC_SEQSTART */ 359 "\1)" NTC /* TC_SEQTERM */ 360 "\1/" NTC /* TC_REGEXP */ 361 "\2>>" "\1>" "\1|" NTC /* TC_OUTRDR */ 362 "\2++" "\2--" NTC /* TC_UOPPOST */ 363 "\2++" "\2--" "\1$" NTC /* TC_UOPPRE1 */ 364 "\2==" "\1=" "\2+=" "\2-=" /* TC_BINOPX */ 307 365 "\2*=" "\2/=" "\2%=" "\2^=" 308 366 "\1+" "\1-" "\3**=" "\2**" … … 311 369 "\1<" "\2!~" "\1~" "\2&&" 312 370 "\2||" "\1?" "\1:" NTC 313 "\2in" NTC 314 "\1," NTC 315 "\1|" NTC 316 "\1+" "\1-" "\1!" NTC /* UOPPRE2 */317 "\1]" NTC 318 "\1{" NTC 319 "\1}" NTC 320 "\1;" NTC 321 "\1\n" NTC 322 "\2if" "\2do" "\3for" "\5break" /* STATX */371 "\2in" NTC /* TC_IN */ 372 "\1," NTC /* TC_COMMA */ 373 "\1|" NTC /* TC_PIPE */ 374 "\1+" "\1-" "\1!" NTC /* TC_UOPPRE2 */ 375 "\1]" NTC /* TC_ARRTERM */ 376 "\1{" NTC /* TC_GRPSTART */ 377 "\1}" NTC /* TC_GRPTERM */ 378 "\1;" NTC /* TC_SEMICOL */ 379 "\1\n" NTC /* TC_NEWLINE */ 380 "\2if" "\2do" "\3for" "\5break" /* TC_STATX */ 323 381 "\10continue" "\6delete" "\5print" 324 382 "\6printf" "\4next" "\10nextfile" 325 383 "\6return" "\4exit" NTC 326 "\5while" NTC 327 "\4else" NTC 328 329 "\3and" "\5compl" "\6lshift" "\2or" 384 "\5while" NTC /* TC_WHILE */ 385 "\4else" NTC /* TC_ELSE */ 386 "\3and" "\5compl" "\6lshift" "\2or" /* TC_BUILTIN */ 330 387 "\6rshift" "\3xor" 331 "\5close" "\6system" "\6fflush" "\5atan2" /* BUILTIN */388 "\5close" "\6system" "\6fflush" "\5atan2" 332 389 "\3cos" "\3exp" "\3int" "\3log" 333 390 "\4rand" "\3sin" "\4sqrt" "\5srand" 334 "\6gensub" "\4gsub" "\5index" "\6length"391 "\6gensub" "\4gsub" "\5index" /* "\6length" was here */ 335 392 "\5match" "\5split" "\7sprintf" "\3sub" 336 393 "\6substr" "\7systime" "\10strftime" "\6mktime" 337 394 "\7tolower" "\7toupper" NTC 338 "\7getline" NTC 339 "\4func" "\10function" NTC 340 "\5BEGIN" NTC 341 "\3END" 395 "\6length" NTC /* TC_LENGTH */ 396 "\7getline" NTC /* TC_GETLINE */ 397 "\4func" "\10function" NTC /* TC_FUNCDECL */ 398 "\5BEGIN" NTC /* TC_BEGIN */ 399 "\3END" /* TC_END */ 342 400 /* compiler adds trailing "\0" */ 343 401 ; 402 403 #define OC_B OC_BUILTIN 344 404 345 405 static const uint32_t tokeninfo[] = { … … 357 417 OC_COMPARE|VV|P(39)|2, OC_MATCH|Sx|P(45)|'!', OC_MATCH|Sx|P(45)|'~', OC_LAND|Vx|P(55), 358 418 OC_LOR|Vx|P(59), OC_TERNARY|Vx|P(64)|'?', OC_COLON|xx|P(67)|':', 359 OC_IN|SV|P(49), /* in*/419 OC_IN|SV|P(49), /* TC_IN */ 360 420 OC_COMMA|SS|P(80), 361 421 OC_PGETLINE|SV|P(37), … … 372 432 ST_WHILE, 373 433 0, /* else */ 374 375 434 OC_B|B_an|P(0x83), OC_B|B_co|P(0x41), OC_B|B_ls|P(0x83), OC_B|B_or|P(0x83), 376 435 OC_B|B_rs|P(0x83), OC_B|B_xo|P(0x83), … … 378 437 OC_FBLTIN|Nx|F_co, OC_FBLTIN|Nx|F_ex, OC_FBLTIN|Nx|F_in, OC_FBLTIN|Nx|F_lg, 379 438 OC_FBLTIN|F_rn, OC_FBLTIN|Nx|F_si, OC_FBLTIN|Nx|F_sq, OC_FBLTIN|Nx|F_sr, 380 OC_B|B_ge|P(0xd6), OC_B|B_gs|P(0xb6), OC_B|B_ix|P(0x9b), OC_FBLTIN|Sx|F_le,439 OC_B|B_ge|P(0xd6), OC_B|B_gs|P(0xb6), OC_B|B_ix|P(0x9b), /* OC_FBLTIN|Sx|F_le, was here */ 381 440 OC_B|B_ma|P(0x89), OC_B|B_sp|P(0x8b), OC_SPRINTF, OC_B|B_su|P(0xb6), 382 441 OC_B|B_ss|P(0x8f), OC_FBLTIN|F_ti, OC_B|B_ti|P(0x0b), OC_B|B_mt|P(0x0b), 383 442 OC_B|B_lo|P(0x49), OC_B|B_up|P(0x49), 443 OC_FBLTIN|Sx|F_le, /* TC_LENGTH */ 384 444 OC_GETLINE|SV|P(0), 385 445 0, 0, 386 446 0, 387 0 /* END */447 0 /* TC_END */ 388 448 }; 389 449 … … 1009 1069 if (t_rollback) { 1010 1070 t_rollback = FALSE; 1011 1012 1071 } else if (concat_inserted) { 1013 1072 concat_inserted = FALSE; 1014 1073 t_tclass = save_tclass; 1015 1074 t_info = save_info; 1016 1017 1075 } else { 1018 1076 p = g_pos; … … 1030 1088 tc = TC_EOF; 1031 1089 debug_printf_parse("%s: token found: TC_EOF\n", __func__); 1032 1033 1090 } else if (*p == '\"') { 1034 1091 /* it's a string */ … … 1046 1103 tc = TC_STRING; 1047 1104 debug_printf_parse("%s: token found:'%s' TC_STRING\n", __func__, t_string); 1048 1049 1105 } else if ((expected & TC_REGEXP) && *p == '/') { 1050 1106 /* it's regexp */ … … 1079 1135 tc = TC_NUMBER; 1080 1136 debug_printf_parse("%s: token found:%f TC_NUMBER\n", __func__, t_double); 1081 1082 1137 } else { 1083 1138 /* search for something known */ … … 1156 1211 1157 1212 /* Are we ready for this? */ 1158 if (!(ltclass & expected)) 1213 if (!(ltclass & expected)) { 1159 1214 syntax_error((ltclass & (TC_NEWLINE | TC_EOF)) ? 1160 1215 EMSG_UNEXP_EOS : EMSG_UNEXP_TOKEN); 1216 } 1161 1217 1162 1218 return ltclass; … … 1325 1381 cn->l.n = condition(); 1326 1382 break; 1383 1384 case TC_LENGTH: 1385 debug_printf_parse("%s: TC_LENGTH\n", __func__); 1386 next_token(TC_SEQSTART | TC_OPTERM | TC_GRPTERM); 1387 rollback_token(); 1388 if (t_tclass & TC_SEQSTART) { 1389 /* It was a "(" token. Handle just like TC_BUILTIN */ 1390 cn->l.n = condition(); 1391 } 1392 break; 1327 1393 } 1328 1394 } … … 1489 1555 n = chain_node(OC_EXEC); 1490 1556 n->a.n = break_ptr; 1557 chain_expr(t_info); 1491 1558 break; 1492 1559 … … 1495 1562 n = chain_node(OC_EXEC); 1496 1563 n->a.n = continue_ptr; 1564 chain_expr(t_info); 1497 1565 break; 1498 1566 … … 1527 1595 seq = &beginseq; 1528 1596 chain_group(); 1529 1530 1597 } else if (tclass & TC_END) { 1531 1598 debug_printf_parse("%s: TC_END\n", __func__); 1532 1599 seq = &endseq; 1533 1600 chain_group(); 1534 1535 1601 } else if (tclass & TC_FUNCDECL) { 1536 1602 debug_printf_parse("%s: TC_FUNCDECL\n", __func__); … … 1550 1616 chain_group(); 1551 1617 clear_array(ahash); 1552 1553 1618 } else if (tclass & TC_OPSEQ) { 1554 1619 debug_printf_parse("%s: TC_OPSEQ\n", __func__); … … 1565 1630 } 1566 1631 cn->r.n = mainseq.last; 1567 1568 1632 } else /* if (tclass & TC_GRPSTART) */ { 1569 1633 debug_printf_parse("%s: TC_GRPSTART(?)\n", __func__); … … 1826 1890 1827 1891 mk_splitter(getvar_s(v), &fsplitter); 1828 1829 1892 } else if (v == intvar[RS]) { 1830 1893 mk_splitter(getvar_s(v), &rsplitter); 1831 1832 1894 } else if (v == intvar[IGNORECASE]) { 1833 1895 icase = istrue(v); 1834 1835 1896 } else { /* $n */ 1836 1897 n = getvar_i(intvar[NF]); … … 2016 2077 const char *s = format; 2017 2078 2018 if (int_as_int && n == ( int)n) {2019 r = snprintf(b, size, "% d", (int)n);2079 if (int_as_int && n == (long long)n) { 2080 r = snprintf(b, size, "%lld", (long long)n); 2020 2081 } else { 2021 2082 do { c = *s; } while (c && *++s); … … 2662 2723 const char *sv_progname; 2663 2724 2664 if (!op->r.f->body.first) 2725 /* The body might be empty, still has to eval the args */ 2726 if (!op->r.n->info && !op->r.f->body.first) 2665 2727 syntax_error(EMSG_UNDEF_FUNC); 2666 2728 … … 2733 2795 switch (opn) { 2734 2796 case F_in: 2735 R_d = ( int)L_d;2797 R_d = (long long)L_d; 2736 2798 break; 2737 2799 … … 2784 2846 2785 2847 case F_le: 2786 if (!op1) 2848 debug_printf_eval("length: L.s:'%s'\n", L.s); 2849 if (!op1) { 2787 2850 L.s = getvar_s(intvar[F0]); 2851 debug_printf_eval("length: L.s='%s'\n", L.s); 2852 } 2853 else if (L.v->type & VF_ARRAY) { 2854 R_d = L.v->x.array->nel; 2855 debug_printf_eval("length: array_len:%d\n", L.v->x.array->nel); 2856 break; 2857 } 2788 2858 R_d = strlen(L.s); 2789 2859 break; … … 2931 3001 if (R_d == 0) 2932 3002 syntax_error(EMSG_DIV_BY_ZERO); 2933 L_d -= ( int)(L_d / R_d) * R_d;3003 L_d -= (long long)(L_d / R_d) * R_d; 2934 3004 break; 2935 3005 } … … 3079 3149 llist_t *list_v = NULL; 3080 3150 llist_t *list_f = NULL; 3151 #if ENABLE_FEATURE_AWK_GNU_EXTENSIONS 3152 llist_t *list_e = NULL; 3153 #endif 3081 3154 int i, j; 3082 3155 var *v; … … 3137 3210 } 3138 3211 } 3139 opt_complementary = "v::f::"; /* -v and -f can occur multiple times */3140 opt = getopt32(argv, "F:v:f:W:", &opt_F, &list_v, &list_f,NULL);3212 opt_complementary = OPTCOMPLSTR_AWK; 3213 opt = getopt32(argv, OPTSTR_AWK, &opt_F, &list_v, &list_f, IF_FEATURE_AWK_GNU_EXTENSIONS(&list_e,) NULL); 3141 3214 argv += optind; 3142 3215 argc -= optind; 3143 if (opt & 0x1) { /* -F */ 3216 if (opt & OPT_W) 3217 bb_error_msg("warning: option -W is ignored"); 3218 if (opt & OPT_F) { 3144 3219 unescape_string_in_place(opt_F); 3145 3220 setvar_s(intvar[FS], opt_F); 3146 3221 } 3147 while (list_v) { /* -v */3222 while (list_v) { 3148 3223 if (!is_assignment(llist_pop(&list_v))) 3149 3224 bb_show_usage(); 3150 3225 } 3151 if (list_f) { /* -f */ 3152 do { 3153 char *s = NULL; 3154 FILE *from_file; 3155 3156 g_progname = llist_pop(&list_f); 3157 from_file = xfopen_stdin(g_progname); 3158 /* one byte is reserved for some trick in next_token */ 3159 for (i = j = 1; j > 0; i += j) { 3160 s = xrealloc(s, i + 4096); 3161 j = fread(s + i, 1, 4094, from_file); 3162 } 3163 s[i] = '\0'; 3164 fclose(from_file); 3165 parse_program(s + 1); 3166 free(s); 3167 } while (list_f); 3168 argc++; 3169 } else { // no -f: take program from 1st parameter 3170 if (!argc) 3226 while (list_f) { 3227 char *s = NULL; 3228 FILE *from_file; 3229 3230 g_progname = llist_pop(&list_f); 3231 from_file = xfopen_stdin(g_progname); 3232 /* one byte is reserved for some trick in next_token */ 3233 for (i = j = 1; j > 0; i += j) { 3234 s = xrealloc(s, i + 4096); 3235 j = fread(s + i, 1, 4094, from_file); 3236 } 3237 s[i] = '\0'; 3238 fclose(from_file); 3239 parse_program(s + 1); 3240 free(s); 3241 } 3242 g_progname = "cmd. line"; 3243 #if ENABLE_FEATURE_AWK_GNU_EXTENSIONS 3244 while (list_e) { 3245 parse_program(llist_pop(&list_e)); 3246 } 3247 #endif 3248 if (!(opt & (OPT_f | OPT_e))) { 3249 if (!*argv) 3171 3250 bb_show_usage(); 3172 g_progname = "cmd. line";3173 3251 parse_program(*argv++); 3174 } 3175 if (opt & 0x8) // -W 3176 bb_error_msg("warning: option -W is ignored"); 3252 argc--; 3253 } 3177 3254 3178 3255 /* fill in ARGV array */ 3179 setvar_i(intvar[ARGC], argc );3256 setvar_i(intvar[ARGC], argc + 1); 3180 3257 setari_u(intvar[ARGV], 0, "awk"); 3181 3258 i = 0; -
branches/3.3/mindi-busybox/editors/cmp.c
r3232 r3621 10 10 /* BB_AUDIT SUSv3 (virtually) compliant -- uses nicer GNU format for -l. */ 11 11 /* http://www.opengroup.org/onlinepubs/007904975/utilities/cmp.html */ 12 13 //config:config CMP 14 //config: bool "cmp" 15 //config: default y 16 //config: help 17 //config: cmp is used to compare two files and returns the result 18 //config: to standard output. 19 20 //kbuild:lib-$(CONFIG_CMP) += cmp.o 21 22 //applet:IF_CMP(APPLET(cmp, BB_DIR_USR_BIN, BB_SUID_DROP)) 12 23 13 24 //usage:#define cmp_trivial_usage -
branches/3.3/mindi-busybox/editors/diff.c
r3232 r3621 77 77 */ 78 78 79 //config:config DIFF 80 //config: bool "diff" 81 //config: default y 82 //config: help 83 //config: diff compares two files or directories and outputs the 84 //config: differences between them in a form that can be given to 85 //config: the patch command. 86 //config: 87 //config:config FEATURE_DIFF_LONG_OPTIONS 88 //config: bool "Enable long options" 89 //config: default y 90 //config: depends on DIFF && LONG_OPTS 91 //config: help 92 //config: Enable use of long options. 93 //config: 94 //config:config FEATURE_DIFF_DIR 95 //config: bool "Enable directory support" 96 //config: default y 97 //config: depends on DIFF 98 //config: help 99 //config: This option enables support for directory and subdirectory 100 //config: comparison. 101 102 //kbuild:lib-$(CONFIG_DIFF) += diff.o 103 104 //applet:IF_DIFF(APPLET(diff, BB_DIR_USR_BIN, BB_SUID_DROP)) 105 79 106 //usage:#define diff_trivial_usage 80 107 //usage: "[-abBdiNqrTstw] [-L LABEL] [-S FILE] [-U LINES] FILE1 FILE2" … … 99 126 100 127 #include "libbb.h" 128 #include "common_bufsiz.h" 101 129 102 130 #if 0 … … 337 365 338 366 struct line { 339 /* 'serial' is not used in the begin ing, so we reuse it367 /* 'serial' is not used in the beginning, so we reuse it 340 368 * to store line offsets, thus reducing memory pressure 341 369 */ … … 407 435 int c = fgetc(ft->ft_fp); 408 436 if (c == EOF) { 409 p rintf("\n\\ No newline at end of file\n");437 puts("\n\\ No newline at end of file"); 410 438 return; 411 439 } … … 632 660 633 661 for (j = 0; j < 2; j++) 634 for (k = v[j].a; k < v[j].b; k++)635 nonempty |= (ix[j][k +1] - ix[j][k] != 1);662 for (k = v[j].a; k <= v[j].b; k++) 663 nonempty |= (ix[j][k] - ix[j][k - 1] != 1); 636 664 637 665 vec = xrealloc_vector(vec, 6, ++idx); … … 666 694 printf(",%d", (a < b) ? b - a + 1 : 0); 667 695 } 668 p rintf(" @@\n");696 puts(" @@"); 669 697 /* 670 698 * Output changes in "unified" diff format--the old and new lines … … 714 742 if (bb_copyfd_eof(fd, fd_tmp) < 0) 715 743 xfunc_die(); 716 if (fd ) /* Prevents closing of stdin */744 if (fd != STDIN_FILENO) 717 745 close(fd); 718 746 fd = fd_tmp; 747 xlseek(fd, 0, SEEK_SET); 719 748 } 720 749 fp[i] = fdopen(fd, "r"); 721 750 } 722 751 752 setup_common_bufsiz(); 723 753 while (1) { 724 754 const size_t sz = COMMON_BUFSIZE / 2; -
branches/3.3/mindi-busybox/editors/ed.c
r3232 r3621 8 8 */ 9 9 10 //config:config ED 11 //config: bool "ed" 12 //config: default y 13 //config: help 14 //config: The original 1970's Unix text editor, from the days of teletypes. 15 //config: Small, simple, evil. Part of SUSv3. If you're not already using 16 //config: this, you don't need it. 17 18 //kbuild:lib-$(CONFIG_ED) += ed.o 19 20 //applet:IF_ED(APPLET(ed, BB_DIR_BIN, BB_SUID_DROP)) 21 10 22 //usage:#define ed_trivial_usage "" 11 23 //usage:#define ed_full_usage "" 12 24 13 25 #include "libbb.h" 26 #include "common_bufsiz.h" 14 27 15 28 typedef struct LINE { … … 24 37 25 38 enum { 26 USERSIZE = sizeof(searchString)> 1024 ? 102427 : sizeof(searchString)- 1, /* max line length typed in by user */39 USERSIZE = COMMON_BUFSIZE > 1024 ? 1024 40 : COMMON_BUFSIZE - 1, /* max line length typed in by user */ 28 41 INITBUF_SIZE = 1024, /* initial buffer size */ 29 42 }; … … 55 68 #define marks (G.marks ) 56 69 #define INIT_G() do { \ 70 setup_common_bufsiz(); \ 57 71 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 58 72 } while (0) … … 195 209 printf("\"%s\"\n", fileName); 196 210 else 197 p rintf("No file name\n");211 puts("No file name"); 198 212 break; 199 213 } … … 721 735 bufUsed += cc; 722 736 bufPtr = bufBase; 723 724 737 } while (cc > 0); 725 738 -
branches/3.3/mindi-busybox/editors/patch.c
r3232 r3621 346 346 // state 2: In hunk: counting initial context lines 347 347 // state 3: In hunk: getting body 348 // Like GNU patch, we don't require a --- line before the +++, and 349 // also allow the --- after the +++ line. 348 350 349 351 int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; … … 371 373 } 372 374 } 373 if (argv[0]) {374 oldname = xstrdup(argv[0]);375 newname = xstrdup(argv[0]);376 }377 375 378 376 // Loop through the lines in the patch … … 413 411 414 412 // Open a new file? 415 if ( !strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) {413 if (is_prefixed_with(patchline, "--- ") || is_prefixed_with(patchline, "+++ ")) { 416 414 char *s, **name = reverse ? &newname : &oldname; 417 415 int i; … … 445 443 // Start a new hunk? Usually @@ -oldline,oldlen +newline,newlen @@ 446 444 // but a missing ,value means the value is 1. 447 } else if (state == 1 && !strncmp("@@ -", patchline, 4)) {445 } else if (state == 1 && is_prefixed_with(patchline, "@@ -")) { 448 446 int i; 449 447 char *s = patchline+4; … … 463 461 state = 2; 464 462 463 // If the --- line is missing or malformed, either oldname 464 // or (for -R) newname could be NULL -- but not both. Like 465 // GNU patch, proceed based on the +++ line, and avoid SEGVs. 466 if (!oldname) 467 oldname = xstrdup("MISSING_FILENAME"); 468 if (!newname) 469 newname = xstrdup("MISSING_FILENAME"); 470 465 471 // If this is the first hunk, open the file. 466 472 if (TT.filein == -1) { … … 477 483 if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum)) { 478 484 name = reverse ? newname : oldname; 479 empty ++;485 empty = 1; 480 486 } 481 487 482 // handle -p path truncation.488 // Handle -p path truncation. 483 489 for (i = 0, s = name; *s;) { 484 490 if ((option_mask32 & FLAG_PATHLEN) && TT.prefix == i) … … 491 497 name = s; 492 498 } 499 // If "patch FILE_TO_PATCH", completely ignore name from patch 500 if (argv[0]) 501 name = argv[0]; 493 502 494 503 if (empty) { -
branches/3.3/mindi-busybox/editors/patch_bbox.c
r2725 r3621 189 189 unsigned dst_last_line = 1; 190 190 191 if ((sscanf(patch_line, "@@ -% d,%d +%d,%d", &src_beg_line, &src_last_line, &dst_beg_line, &dst_last_line) < 3)192 && (sscanf(patch_line, "@@ -% d +%d,%d", &src_beg_line, &dst_beg_line, &dst_last_line) < 2)191 if ((sscanf(patch_line, "@@ -%u,%u +%u,%u", &src_beg_line, &src_last_line, &dst_beg_line, &dst_last_line) < 3) 192 && (sscanf(patch_line, "@@ -%u +%u,%u", &src_beg_line, &dst_beg_line, &dst_last_line) < 2) 193 193 ) { 194 194 /* No more hunks for this file */ -
branches/3.3/mindi-busybox/editors/sed.c
r3232 r3621 23 23 * resulting sed_cmd_t structures are appended to a linked list 24 24 * (G.sed_cmd_head/G.sed_cmd_tail). 25 *26 * add_input_file() adds a FILE* to the list of input files. We need to27 * know all input sources ahead of time to find the last line for the $ match.28 25 * 29 26 * process_files() does actual sedding, reading data lines from each input FILE* … … 57 54 * http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html 58 55 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html 56 * http://sed.sourceforge.net/sedfaq3.html 59 57 */ 60 58 59 //config:config SED 60 //config: bool "sed" 61 //config: default y 62 //config: help 63 //config: sed is used to perform text transformations on a file 64 //config: or input from a pipeline. 65 66 //kbuild:lib-$(CONFIG_SED) += sed.o 67 68 //applet:IF_SED(APPLET(sed, BB_DIR_BIN, BB_SUID_DROP)) 69 61 70 //usage:#define sed_trivial_usage 62 //usage: "[-inr ] [-f FILE]... [-e CMD]... [FILE]...\n"63 //usage: "or: sed [-inr ] CMD [FILE]..."71 //usage: "[-inrE] [-f FILE]... [-e CMD]... [FILE]...\n" 72 //usage: "or: sed [-inrE] CMD [FILE]..." 64 73 //usage:#define sed_full_usage "\n\n" 65 74 //usage: " -e CMD Add CMD to sed commands to be executed" … … 68 77 //usage: "\n Optionally back files up, appending SFX" 69 78 //usage: "\n -n Suppress automatic printing of pattern space" 70 //usage: "\n -r Use extended regex syntax"79 //usage: "\n -r,-E Use extended regex syntax" 71 80 //usage: "\n" 72 81 //usage: "\nIf no -e or -f, the first non-option argument is the sed command string." … … 78 87 79 88 #include "libbb.h" 89 #include "common_bufsiz.h" 80 90 #include "xregex.h" 81 91 … … 102 112 int beg_line; /* 'sed 1p' 0 == apply commands to all lines */ 103 113 int beg_line_orig; /* copy of the above, needed for -i */ 104 int end_line; /* 'sed 1,3p' 0 == one line only. -1 = last line ($) */ 105 106 FILE *sw_file; /* File (sw) command writes to, -1 for none. */ 114 int end_line; /* 'sed 1,3p' 0 == one line only. -1 = last line ($). -2-N = +N */ 115 int end_line_orig; 116 117 FILE *sw_file; /* File (sw) command writes to, NULL for none. */ 107 118 char *string; /* Data string for (saicytb) commands. */ 108 119 … … 125 136 /* options */ 126 137 int be_quiet, regex_type; 138 127 139 FILE *nonstdout; 128 140 char *outname, *hold_space; 129 130 /* List of input files */ 131 int input_file_count, current_input_file; 132 FILE **input_file_list; 141 smallint exitcode; 142 143 /* list of input files */ 144 int current_input_file, last_input_file; 145 char **input_file_list; 146 FILE *current_fp; 133 147 134 148 regmatch_t regmatch[10]; … … 138 152 sed_cmd_t *sed_cmd_head, **sed_cmd_tail; 139 153 140 /* Linked list of append lines */154 /* linked list of append lines */ 141 155 llist_t *append_head; 142 156 … … 149 163 } pipeline; 150 164 } FIX_ALIASING; 151 #define G (*(struct globals*)&bb_common_bufsiz1) 152 struct BUG_G_too_big { 153 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1]; 154 }; 165 #define G (*(struct globals*)bb_common_bufsiz1) 155 166 #define INIT_G() do { \ 167 setup_common_bufsiz(); \ 168 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \ 156 169 G.sed_cmd_tail = &G.sed_cmd_head; \ 157 170 } while (0) … … 169 182 170 183 if (sed_cmd->sw_file) 171 xprint_and_close_file(sed_cmd->sw_file);184 fclose(sed_cmd->sw_file); 172 185 173 186 if (sed_cmd->beg_match) { … … 190 203 free(G.hold_space); 191 204 192 while (G.current_input_file < G.input_file_count)193 fclose(G. input_file_list[G.current_input_file++]);205 if (G.current_fp) 206 fclose(G.current_fp); 194 207 } 195 208 #else … … 206 219 /* strcpy, replacing "\from" with 'to'. If to is NUL, replacing "\any" with 'any' */ 207 220 208 static void parse_escapes(char *dest, const char *string, int len, char from, char to) 209 { 221 static unsigned parse_escapes(char *dest, const char *string, int len, char from, char to) 222 { 223 char *d = dest; 210 224 int i = 0; 225 226 if (len == -1) 227 len = strlen(string); 211 228 212 229 while (i < len) { 213 230 if (string[i] == '\\') { 214 231 if (!to || string[i+1] == from) { 215 *dest++ = to ? to : string[i+1]; 232 if ((*d = to ? to : string[i+1]) == '\0') 233 return d - dest; 216 234 i += 2; 235 d++; 217 236 continue; 218 237 } 219 *dest++ = string[i++]; 220 } 221 /* TODO: is it safe wrt a string with trailing '\\' ? */ 222 *dest++ = string[i++]; 223 } 224 *dest = '\0'; 238 i++; /* skip backslash in string[] */ 239 *d++ = '\\'; 240 /* fall through: copy next char verbatim */ 241 } 242 if ((*d = string[i++]) == '\0') 243 return d - dest; 244 d++; 245 } 246 *d = '\0'; 247 return d - dest; 225 248 } 226 249 … … 233 256 /* GNU sed also recognizes \t and \r */ 234 257 for (s = "\nn\tt\rr"; *s; s += 2) { 235 parse_escapes(dest, string, len, s[1], s[0]);258 len = parse_escapes(dest, string, len, s[1], s[0]); 236 259 string = dest; 237 len = strlen(dest);238 260 } 239 261 return dest; … … 331 353 temp = copy_parsing_escapes(pos, next); 332 354 *regex = xzalloc(sizeof(regex_t)); 333 xregcomp(*regex, temp, G.regex_type |REG_NEWLINE);355 xregcomp(*regex, temp, G.regex_type); 334 356 free(temp); 335 357 /* Move position to next character after last delimiter */ … … 371 393 /* 372 394 * A substitution command should look something like this: 373 * s/match/replace/ #g Ipw395 * s/match/replace/ #giIpw 374 396 * || | ||| 375 397 * mandatory optional … … 385 407 386 408 sed_cmd->which_match = 1; 409 dbg("s flags:'%s'", substr + idx + 1); 387 410 while (substr[++idx]) { 411 dbg("s flag:'%c'", substr[idx]); 388 412 /* Parse match number */ 389 413 if (isdigit(substr[idx])) { … … 393 417 /* FIXME: error check? */ 394 418 sed_cmd->which_match = (unsigned)strtol(substr+idx, (char**) &pos, 10); 395 idx = pos - substr ;419 idx = pos - substr - 1; 396 420 } 397 421 continue; … … 414 438 case 'w': 415 439 { 416 char *temp; 417 idx += parse_file_cmd(/*sed_cmd,*/ substr+idx, &temp); 440 char *fname; 441 idx += parse_file_cmd(/*sed_cmd,*/ substr+idx+1, &fname); 442 sed_cmd->sw_file = xfopen_for_write(fname); 443 sed_cmd->sw_last_char = '\n'; 444 free(fname); 418 445 break; 419 446 } 420 447 /* Ignore case (gnu exension) */ 448 case 'i': 421 449 case 'I': 422 450 cflags |= REG_ICASE; … … 432 460 goto out; 433 461 default: 462 dbg("s bad flags:'%s'", substr + idx); 434 463 bb_error_msg_and_die("bad option in substitution expression"); 435 464 } … … 454 483 static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr) 455 484 { 456 static const char cmd_letters[] = "saicrw:btTydDgGhHlnNpPqx={}";485 static const char cmd_letters[] ALIGN1 = "saicrw:btTydDgGhHlnNpPqx={}"; 457 486 enum { 458 487 IDX_s = 0, … … 485 514 IDX_nul 486 515 }; 487 struct chk { char chk[sizeof(cmd_letters)-1 == IDX_nul ? 1 : -1]; }; 488 489 unsigned idx = strchrnul(cmd_letters, sed_cmd->cmd) - cmd_letters; 516 unsigned idx; 517 518 BUILD_BUG_ON(sizeof(cmd_letters)-1 != IDX_nul); 519 520 idx = strchrnul(cmd_letters, sed_cmd->cmd) - cmd_letters; 490 521 491 522 /* handle (s)ubstitution command */ … … 495 526 /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */ 496 527 else if (idx <= IDX_c) { /* a,i,c */ 528 unsigned len; 529 497 530 if (idx < IDX_c) { /* a,i */ 498 531 if (sed_cmd->end_line || sed_cmd->end_match) … … 508 541 cmdstr++; 509 542 } 510 sed_cmd->string = xstrdup(cmdstr); 543 len = strlen(cmdstr); 544 sed_cmd->string = copy_parsing_escapes(cmdstr, len); 545 cmdstr += len; 511 546 /* "\anychar" -> "anychar" */ 512 parse_escapes(sed_cmd->string, sed_cmd->string, strlen(cmdstr), '\0', '\0'); 513 cmdstr += strlen(cmdstr); 547 parse_escapes(sed_cmd->string, sed_cmd->string, -1, '\0', '\0'); 514 548 } 515 549 /* handle file cmds: (r)ead */ … … 543 577 cmdstr += parse_regex_delim(cmdstr, &match, &replace)+1; 544 578 /* \n already parsed, but \delimiter needs unescaping. */ 545 parse_escapes(match, match, strlen(match), i, i);546 parse_escapes(replace, replace, strlen(replace), i, i);579 parse_escapes(match, match, -1, i, i); 580 parse_escapes(replace, replace, -1, i, i); 547 581 548 582 sed_cmd->string = xzalloc((strlen(match) + 1) * 2); … … 626 660 627 661 cmdstr++; 628 idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match); 629 if (!idx) 662 if (*cmdstr == '+' && isdigit(cmdstr[1])) { 663 /* http://sed.sourceforge.net/sedfaq3.html#s3.3 664 * Under GNU sed 3.02+, ssed, and sed15+, <address2> 665 * may also be a notation of the form +num, 666 * indicating the next num lines after <address1> is 667 * matched. 668 * GNU sed 4.2.1 accepts even "+" (meaning "+0"). 669 * We don't (we check for isdigit, see above), think 670 * about the "+-3" case. 671 */ 672 char *end; 673 /* code is smaller compared to using &cmdstr here: */ 674 idx = strtol(cmdstr+1, &end, 10); 675 sed_cmd->end_line = -2 - idx; 676 cmdstr = end; 677 } else { 678 idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match); 679 cmdstr += idx; 680 idx--; /* if 0, trigger error check below */ 681 } 682 if (idx < 0) 630 683 bb_error_msg_and_die("no address after comma"); 631 cmdstr += idx;684 sed_cmd->end_line_orig = sed_cmd->end_line; 632 685 } 633 686 … … 649 702 sed_cmd->cmd = *cmdstr++; 650 703 cmdstr = parse_cmd_args(sed_cmd, cmdstr); 704 705 /* cmdstr now points past args. 706 * GNU sed requires a separator, if there are more commands, 707 * else it complains "char N: extra characters after command". 708 * Example: "sed 'p;d'". We also allow "sed 'pd'". 709 */ 651 710 652 711 /* Add the command to the command array */ … … 843 902 static void append(char *s) 844 903 { 845 llist_add_to_end(&G.append_head, xstrdup(s)); 846 } 847 848 static void flush_append(void) 849 { 850 char *data; 851 852 /* Output appended lines. */ 853 while ((data = (char *)llist_pop(&G.append_head))) { 854 fprintf(G.nonstdout, "%s\n", data); 855 free(data); 856 } 857 } 858 859 static void add_input_file(FILE *file) 860 { 861 G.input_file_list = xrealloc_vector(G.input_file_list, 2, G.input_file_count); 862 G.input_file_list[G.input_file_count++] = file; 863 } 864 865 /* Get next line of input from G.input_file_list, flushing append buffer and 866 * noting if we ran out of files without a newline on the last line we read. 904 llist_add_to_end(&G.append_head, s); 905 } 906 907 /* Output line of text. */ 908 /* Note: 909 * The tricks with NO_EOL_CHAR and last_puts_char are there to emulate gnu sed. 910 * Without them, we had this: 911 * echo -n thingy >z1 912 * echo -n again >z2 913 * >znull 914 * sed "s/i/z/" z1 z2 znull | hexdump -vC 915 * output: 916 * gnu sed 4.1.5: 917 * 00000000 74 68 7a 6e 67 79 0a 61 67 61 7a 6e |thzngy.agazn| 918 * bbox: 919 * 00000000 74 68 7a 6e 67 79 61 67 61 7a 6e |thzngyagazn| 867 920 */ 868 921 enum { … … 870 923 LAST_IS_NUL = 2, 871 924 }; 872 static char *get_next_line(char *gets_char) 925 static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char) 926 { 927 char lpc = *last_puts_char; 928 929 /* Need to insert a '\n' between two files because first file's 930 * last line wasn't terminated? */ 931 if (lpc != '\n' && lpc != '\0') { 932 fputc('\n', file); 933 lpc = '\n'; 934 } 935 fputs(s, file); 936 937 /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */ 938 if (s[0]) 939 lpc = 'x'; 940 941 /* had trailing '\0' and it was last char of file? */ 942 if (last_gets_char == LAST_IS_NUL) { 943 fputc('\0', file); 944 lpc = 'x'; /* */ 945 } else 946 /* had trailing '\n' or '\0'? */ 947 if (last_gets_char != NO_EOL_CHAR) { 948 fputc(last_gets_char, file); 949 lpc = last_gets_char; 950 } 951 952 if (ferror(file)) { 953 xfunc_error_retval = 4; /* It's what gnu sed exits with... */ 954 bb_error_msg_and_die(bb_msg_write_error); 955 } 956 *last_puts_char = lpc; 957 } 958 959 static void flush_append(char *last_puts_char) 960 { 961 char *data; 962 963 /* Output appended lines. */ 964 while ((data = (char *)llist_pop(&G.append_head)) != NULL) { 965 /* Append command does not respect "nonterminated-ness" 966 * of last line. Try this: 967 * $ echo -n "woot" | sed -e '/woot/a woo' - 968 * woot 969 * woo 970 * (both lines are terminated with \n) 971 * Therefore we do not propagate "last_gets_char" here, 972 * pass '\n' instead: 973 */ 974 puts_maybe_newline(data, G.nonstdout, last_puts_char, '\n'); 975 free(data); 976 } 977 } 978 979 /* Get next line of input from G.input_file_list, flushing append buffer and 980 * noting if we ran out of files without a newline on the last line we read. 981 */ 982 static char *get_next_line(char *gets_char, char *last_puts_char) 873 983 { 874 984 char *temp = NULL; … … 876 986 char gc; 877 987 878 flush_append( );988 flush_append(last_puts_char); 879 989 880 990 /* will be returned if last line in the file 881 991 * doesn't end with either '\n' or '\0' */ 882 992 gc = NO_EOL_CHAR; 883 while (G.current_input_file < G.input_file_count) { 884 FILE *fp = G.input_file_list[G.current_input_file]; 993 for (; G.current_input_file <= G.last_input_file; G.current_input_file++) { 994 FILE *fp = G.current_fp; 995 if (!fp) { 996 const char *path = G.input_file_list[G.current_input_file]; 997 fp = stdin; 998 if (path != bb_msg_standard_input) { 999 fp = fopen_or_warn(path, "r"); 1000 if (!fp) { 1001 G.exitcode = EXIT_FAILURE; 1002 continue; 1003 } 1004 } 1005 G.current_fp = fp; 1006 } 885 1007 /* Read line up to a newline or NUL byte, inclusive, 886 1008 * return malloc'ed char[]. length of the chunk read … … 913 1035 } 914 1036 /* Close this file and advance to next one */ 915 fclose (fp);916 G.current_ input_file++;1037 fclose_if_not_stdin(fp); 1038 G.current_fp = NULL; 917 1039 } 918 1040 *gets_char = gc; 919 1041 return temp; 920 }921 922 /* Output line of text. */923 /* Note:924 * The tricks with NO_EOL_CHAR and last_puts_char are there to emulate gnu sed.925 * Without them, we had this:926 * echo -n thingy >z1927 * echo -n again >z2928 * >znull929 * sed "s/i/z/" z1 z2 znull | hexdump -vC930 * output:931 * gnu sed 4.1.5:932 * 00000000 74 68 7a 6e 67 79 0a 61 67 61 7a 6e |thzngy.agazn|933 * bbox:934 * 00000000 74 68 7a 6e 67 79 61 67 61 7a 6e |thzngyagazn|935 */936 static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char)937 {938 char lpc = *last_puts_char;939 940 /* Need to insert a '\n' between two files because first file's941 * last line wasn't terminated? */942 if (lpc != '\n' && lpc != '\0') {943 fputc('\n', file);944 lpc = '\n';945 }946 fputs(s, file);947 948 /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */949 if (s[0])950 lpc = 'x';951 952 /* had trailing '\0' and it was last char of file? */953 if (last_gets_char == LAST_IS_NUL) {954 fputc('\0', file);955 lpc = 'x'; /* */956 } else957 /* had trailing '\n' or '\0'? */958 if (last_gets_char != NO_EOL_CHAR) {959 fputc(last_gets_char, file);960 lpc = last_gets_char;961 }962 963 if (ferror(file)) {964 xfunc_error_retval = 4; /* It's what gnu sed exits with... */965 bb_error_msg_and_die(bb_msg_write_error);966 }967 *last_puts_char = lpc;968 1042 } 969 1043 … … 990 1064 991 1065 /* Prime the pump */ 992 next_line = get_next_line(&next_gets_char );1066 next_line = get_next_line(&next_gets_char, &last_puts_char); 993 1067 994 1068 /* Go through every line in each file */ … … 1004 1078 /* Read one line in advance so we can act on the last line, 1005 1079 * the '$' address */ 1006 next_line = get_next_line(&next_gets_char );1080 next_line = get_next_line(&next_gets_char, &last_puts_char); 1007 1081 linenum++; 1008 1082 … … 1063 1137 1064 1138 if (matched) { 1139 if (sed_cmd->end_line <= -2) { 1140 /* address2 is +N, i.e. N lines from beg_line */ 1141 sed_cmd->end_line = linenum + (-sed_cmd->end_line - 2); 1142 } 1065 1143 /* once matched, "n,xxx" range is dead, disabling it */ 1066 1144 if (sed_cmd->beg_line > 0) { 1067 1145 sed_cmd->beg_line = -2; 1068 1146 } 1147 dbg("end1:%d", sed_cmd->end_line ? sed_cmd->end_line == -1 1148 ? !next_line : (sed_cmd->end_line <= linenum) 1149 : !sed_cmd->end_match); 1150 dbg("end2:%d", sed_cmd->end_match && old_matched 1151 && !regexec(sed_cmd->end_match,pattern_space, 0, NULL, 0)); 1069 1152 sed_cmd->in_match = !( 1070 1153 /* has the ending line come, or is this a single address command? */ … … 1176 1259 /* Append line to linked list to be printed later */ 1177 1260 case 'a': 1178 append( sed_cmd->string);1261 append(xstrdup(sed_cmd->string)); 1179 1262 break; 1180 1263 … … 1198 1281 if (rfile) { 1199 1282 char *line; 1200 1201 1283 while ((line = xmalloc_fgetline(rfile)) 1202 1284 != NULL) 1203 1285 append(line); 1204 xprint_and_close_file(rfile);1286 fclose(rfile); 1205 1287 } 1206 1288 … … 1223 1305 pattern_space = next_line; 1224 1306 last_gets_char = next_gets_char; 1225 next_line = get_next_line(&next_gets_char );1307 next_line = get_next_line(&next_gets_char, &last_puts_char); 1226 1308 substituted = 0; 1227 1309 linenum++; … … 1259 1341 strcpy(pattern_space + len+1, next_line); 1260 1342 last_gets_char = next_gets_char; 1261 next_line = get_next_line(&next_gets_char );1343 next_line = get_next_line(&next_gets_char, &last_puts_char); 1262 1344 linenum++; 1263 1345 break; … … 1363 1445 /* Delete and such jump here. */ 1364 1446 discard_line: 1365 flush_append( );1447 flush_append(&last_puts_char /*,last_gets_char*/); 1366 1448 free(pattern_space); 1367 1449 … … 1372 1454 * newlines. This counts as multiple command lines. 1373 1455 * However, newline can be escaped: 's/e/z\<newline>z/' 1374 * We check forthis.1456 * add_cmd() handles this. 1375 1457 */ 1376 1458 … … 1382 1464 do { 1383 1465 eol = strchr(cmdstr, '\n'); 1384 next: 1385 if (eol) { 1386 /* Count preceding slashes */ 1387 int slashes = 0; 1388 char *sl = eol; 1389 1390 while (sl != cmdstr && *--sl == '\\') 1391 slashes++; 1392 /* Odd number of preceding slashes - newline is escaped */ 1393 if (slashes & 1) { 1394 overlapping_strcpy(eol - 1, eol); 1395 eol = strchr(eol, '\n'); 1396 goto next; 1397 } 1466 if (eol) 1398 1467 *eol = '\0'; 1399 }1400 1468 add_cmd(cmdstr); 1401 1469 cmdstr = eol + 1; … … 1422 1490 #endif 1423 1491 1424 int status = EXIT_SUCCESS;1425 1426 1492 INIT_G(); 1427 1493 … … 1444 1510 1445 1511 /* -i must be first, to match OPT_in_place definition */ 1446 opt = getopt32(argv, "i::rne:f:", &opt_i, &opt_e, &opt_f, 1512 /* -E is a synonym of -r: 1513 * GNU sed 4.2.1 mentions it in neither --help 1514 * nor manpage, but does recognize it. 1515 */ 1516 opt = getopt32(argv, "i::rEne:f:", &opt_i, &opt_e, &opt_f, 1447 1517 &G.be_quiet); /* counter for -n */ 1448 1518 //argc -= optind; … … 1451 1521 atexit(cleanup_outname); 1452 1522 } 1453 if (opt & 0x2) G.regex_type |= REG_EXTENDED; // -r 1454 //if (opt & 0x4) G.be_quiet++; // -n 1523 if (opt & (2|4)) 1524 G.regex_type |= REG_EXTENDED; // -r or -E 1525 //if (opt & 8) 1526 // G.be_quiet++; // -n (implemented with a counter instead) 1455 1527 while (opt_e) { // -e 1456 1528 add_cmd_block(llist_pop(&opt_e)); … … 1459 1531 char *line; 1460 1532 FILE *cmdfile; 1461 cmdfile = xfopen_ for_read(llist_pop(&opt_f));1533 cmdfile = xfopen_stdin(llist_pop(&opt_f)); 1462 1534 while ((line = xmalloc_fgetline(cmdfile)) != NULL) { 1463 1535 add_cmd(line); 1464 1536 free(line); 1465 1537 } 1466 fclose (cmdfile);1538 fclose_if_not_stdin(cmdfile); 1467 1539 } 1468 1540 /* if we didn't get a pattern from -e or -f, use argv[0] */ 1469 if (!(opt & 0x 18)) {1541 if (!(opt & 0x30)) { 1470 1542 if (!*argv) 1471 1543 bb_show_usage(); … … 1481 1553 * files were specified or '-' was specified, take input from stdin. 1482 1554 * Otherwise, we process all the files specified. */ 1483 if (argv[0] == NULL) { 1555 G.input_file_list = argv; 1556 if (!argv[0]) { 1484 1557 if (opt & OPT_in_place) 1485 1558 bb_error_msg_and_die(bb_msg_requires_arg, "-i"); 1486 add_input_file(stdin); 1559 argv[0] = (char*)bb_msg_standard_input; 1560 /* G.last_input_file = 0; - already is */ 1487 1561 } else { 1488 int i;1489 1490 for ( i = 0; argv[i]; i++) {1562 goto start; 1563 1564 for (; *argv; argv++) { 1491 1565 struct stat statbuf; 1492 1566 int nonstdoutfd; 1493 FILE *file;1494 1567 sed_cmd_t *sed_cmd; 1495 1568 1496 if (LONE_DASH(argv[i]) && !(opt & OPT_in_place)) { 1497 add_input_file(stdin); 1498 process_files(); 1569 G.last_input_file++; 1570 start: 1571 if (!(opt & OPT_in_place)) { 1572 if (LONE_DASH(*argv)) { 1573 *argv = (char*)bb_msg_standard_input; 1574 process_files(); 1575 } 1499 1576 continue; 1500 1577 } 1501 file = fopen_or_warn(argv[i], "r"); 1502 if (!file) { 1503 status = EXIT_FAILURE; 1578 1579 /* -i: process each FILE separately: */ 1580 1581 if (stat(*argv, &statbuf) != 0) { 1582 bb_simple_perror_msg(*argv); 1583 G.exitcode = EXIT_FAILURE; 1584 G.current_input_file++; 1504 1585 continue; 1505 1586 } 1506 add_input_file(file); 1507 if (!(opt & OPT_in_place)) { 1508 continue; 1509 } 1510 1511 /* -i: process each FILE separately: */ 1512 1513 G.outname = xasprintf("%sXXXXXX", argv[i]); 1587 G.outname = xasprintf("%sXXXXXX", *argv); 1514 1588 nonstdoutfd = xmkstemp(G.outname); 1515 1589 G.nonstdout = xfdopen_for_write(nonstdoutfd); 1516 1517 1590 /* Set permissions/owner of output file */ 1518 fstat(fileno(file), &statbuf);1519 1591 /* chmod'ing AFTER chown would preserve suid/sgid bits, 1520 1592 * but GNU sed 4.2.1 does not preserve them either */ … … 1527 1599 1528 1600 if (opt_i) { 1529 char *backupname = xasprintf("%s%s", argv[i], opt_i);1530 xrename( argv[i], backupname);1601 char *backupname = xasprintf("%s%s", *argv, opt_i); 1602 xrename(*argv, backupname); 1531 1603 free(backupname); 1532 1604 } 1533 /* else unlink( argv[i]); - rename below does this */1534 xrename(G.outname, argv[i]); //TODO: rollback backup on error?1605 /* else unlink(*argv); - rename below does this */ 1606 xrename(G.outname, *argv); //TODO: rollback backup on error? 1535 1607 free(G.outname); 1536 1608 G.outname = NULL; 1537 1609 1538 /* Re-enable disabled range matches */1610 /* Fix disabled range matches and mangled ",+N" ranges */ 1539 1611 for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) { 1540 1612 sed_cmd->beg_line = sed_cmd->beg_line_orig; 1613 sed_cmd->end_line = sed_cmd->end_line_orig; 1541 1614 } 1542 1615 } 1543 1616 /* Here, to handle "sed 'cmds' nonexistent_file" case we did: 1544 * if (G.current_input_file >= G.input_file_count)1545 * return status;1617 * if (G.current_input_file[G.current_input_file] == NULL) 1618 * return G.exitcode; 1546 1619 * but it's not needed since process_files() works correctly 1547 1620 * in this case too. */ 1548 1621 } 1622 1549 1623 process_files(); 1550 1624 1551 return status;1552 } 1625 return G.exitcode; 1626 } -
branches/3.3/mindi-busybox/editors/vi.c
r3232 r3621 18 18 * More intelligence in refresh() 19 19 * ":r !cmd" and "!cmd" to filter text through an external command 20 * A true "undo" facility21 20 * An "ex" line oriented mode- maybe using "cmdedit" 22 21 */ … … 137 136 //config: 138 137 //config: This is not clean but helps a lot on serial lines and such. 138 //config:config FEATURE_VI_UNDO 139 //config: bool "Support undo command 'u'" 140 //config: default y 141 //config: depends on VI 142 //config: help 143 //config: Support the 'u' command to undo insertion, deletion, and replacement 144 //config: of text. 145 //config:config FEATURE_VI_UNDO_QUEUE 146 //config: bool "Enable undo operation queuing" 147 //config: default y 148 //config: depends on FEATURE_VI_UNDO 149 //config: help 150 //config: The vi undo functions can use an intermediate queue to greatly lower 151 //config: malloc() calls and overhead. When the maximum size of this queue is 152 //config: reached, the contents of the queue are committed to the undo stack. 153 //config: This increases the size of the undo code and allows some undo 154 //config: operations (especially un-typing/backspacing) to be far more useful. 155 //config:config FEATURE_VI_UNDO_QUEUE_MAX 156 //config: int "Maximum undo character queue size" 157 //config: default 256 158 //config: range 32 65536 159 //config: depends on FEATURE_VI_UNDO_QUEUE 160 //config: help 161 //config: This option sets the number of bytes used at runtime for the queue. 162 //config: Smaller values will create more undo objects and reduce the amount 163 //config: of typed or backspaced characters that are grouped into one undo 164 //config: operation; larger values increase the potential size of each undo 165 //config: and will generally malloc() larger objects and less frequently. 166 //config: Unless you want more (or less) frequent "undo points" while typing, 167 //config: you should probably leave this unchanged. 139 168 140 169 //applet:IF_VI(APPLET(vi, BB_DIR_BIN, BB_SUID_DROP)) … … 223 252 // vda: removed "aAiIs" as they switch us into insert mode 224 253 // and remembering input for replay after them makes no sense 225 static const char modifying_cmds[] = "cCdDJoOpPrRxX<>~";254 static const char modifying_cmds[] ALIGN1 = "cCdDJoOpPrRxX<>~"; 226 255 #endif 227 256 … … 278 307 // [code audit says "can be 0, 1 or 2 only"] 279 308 smallint cmd_mode; // 0=command 1=insert 2=replace 280 int file_modified; // buffer contents changed (counter, not flag!)281 int last_ file_modified;// = -1;309 int modified_count; // buffer contents changed if !0 310 int last_modified_count; // = -1; 282 311 int save_argc; // how many file names on cmd line 283 312 int cmdcnt; // repetition count … … 348 377 349 378 char scr_out_buf[MAX_SCR_COLS + MAX_TABSTOP * 2]; 379 #if ENABLE_FEATURE_VI_UNDO 380 // undo_push() operations 381 #define UNDO_INS 0 382 #define UNDO_DEL 1 383 #define UNDO_INS_CHAIN 2 384 #define UNDO_DEL_CHAIN 3 385 // UNDO_*_QUEUED must be equal to UNDO_xxx ORed with UNDO_QUEUED_FLAG 386 #define UNDO_QUEUED_FLAG 4 387 #define UNDO_INS_QUEUED 4 388 #define UNDO_DEL_QUEUED 5 389 #define UNDO_USE_SPOS 32 390 #define UNDO_EMPTY 64 391 // Pass-through flags for functions that can be undone 392 #define NO_UNDO 0 393 #define ALLOW_UNDO 1 394 #define ALLOW_UNDO_CHAIN 2 395 # if ENABLE_FEATURE_VI_UNDO_QUEUE 396 #define ALLOW_UNDO_QUEUED 3 397 char undo_queue_state; 398 int undo_q; 399 char *undo_queue_spos; // Start position of queued operation 400 char undo_queue[CONFIG_FEATURE_VI_UNDO_QUEUE_MAX]; 401 # else 402 // If undo queuing disabled, don't invoke the missing queue logic 403 #define ALLOW_UNDO_QUEUED 1 404 # endif 405 406 struct undo_object { 407 struct undo_object *prev; // Linking back avoids list traversal (LIFO) 408 int start; // Offset where the data should be restored/deleted 409 int length; // total data size 410 uint8_t u_type; // 0=deleted, 1=inserted, 2=swapped 411 char undo_text[1]; // text that was deleted (if deletion) 412 } *undo_stack_tail; 413 #endif /* ENABLE_FEATURE_VI_UNDO */ 350 414 }; 351 415 #define G (*ptr_to_globals) … … 359 423 #define editing (G.editing ) 360 424 #define cmd_mode (G.cmd_mode ) 361 #define file_modified (G.file_modified)362 #define last_ file_modified (G.last_file_modified)425 #define modified_count (G.modified_count ) 426 #define last_modified_count (G.last_modified_count) 363 427 #define save_argc (G.save_argc ) 364 428 #define cmdcnt (G.cmdcnt ) … … 409 473 #define get_input_line__buf (G.get_input_line__buf) 410 474 475 #if ENABLE_FEATURE_VI_UNDO 476 #define undo_stack_tail (G.undo_stack_tail ) 477 # if ENABLE_FEATURE_VI_UNDO_QUEUE 478 #define undo_queue_state (G.undo_queue_state) 479 #define undo_q (G.undo_q ) 480 #define undo_queue (G.undo_queue ) 481 #define undo_queue_spos (G.undo_queue_spos ) 482 # endif 483 #endif 484 411 485 #define INIT_G() do { \ 412 486 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 413 last_ file_modified= -1; \487 last_modified_count = -1; \ 414 488 /* "" but has space for 2 chars: */ \ 415 489 IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \ … … 417 491 418 492 419 static int init_text_buffer(char *); // init from file or create new420 493 static void edit_file(char *); // edit one file 421 494 static void do_cmd(int); // execute a command … … 428 501 static char *end_screen(void); // get pointer to last char on screen 429 502 static int count_lines(char *, char *); // count line from start to stop 430 static char *find_line(int); // find begin ing of line #li503 static char *find_line(int); // find beginning of line #li 431 504 static char *move_to_col(char *, int); // move "p" to column l 432 505 static void dot_left(void); // move dot left- dont leave line … … 438 511 static void dot_scroll(int, int); // move the screen up or down 439 512 static void dot_skip_over_ws(void); // move dot pat WS 440 static void dot_delete(void); // delete the char at 'dot'441 513 static char *bound_dot(char *); // make sure text[0] <= P < "end" 442 514 static char *new_screen(int, int); // malloc virtual screen memory 443 static char *char_insert(char *, char); // insert the char c at 'p' 515 #if !ENABLE_FEATURE_VI_UNDO 516 #define char_insert(a,b,c) char_insert(a,b) 517 #endif 518 static char *char_insert(char *, char, int); // insert the char c at 'p' 444 519 // might reallocate text[]! use p += stupid_insert(p, ...), 445 520 // and be careful to not use pointers into potentially freed text[]! … … 449 524 static char *skip_thing(char *, int, int, int); // skip some object 450 525 static char *find_pair(char *, char); // find matching pair () [] {} 451 static char *text_hole_delete(char *, char *); // at "p", delete a 'size' byte hole 526 #if !ENABLE_FEATURE_VI_UNDO 527 #define text_hole_delete(a,b,c) text_hole_delete(a,b) 528 #endif 529 static char *text_hole_delete(char *, char *, int); // at "p", delete a 'size' byte hole 452 530 // might reallocate text[]! use p += text_hole_make(p, ...), 453 531 // and be careful to not use pointers into potentially freed text[]! 454 532 static uintptr_t text_hole_make(char *, int); // at "p", make a 'size' byte hole 455 static char *yank_delete(char *, char *, int, int); // yank text[] into register then delete 533 #if !ENABLE_FEATURE_VI_UNDO 534 #define yank_delete(a,b,c,d,e) yank_delete(a,b,c,d) 535 #endif 536 static char *yank_delete(char *, char *, int, int, int); // yank text[] into register then delete 456 537 static void show_help(void); // display some help info 457 538 static void rawmode(void); // set "raw" mode on tty … … 461 542 static int readit(void); // read (maybe cursor) key from stdin 462 543 static int get_one_char(void); // read 1 char from stdin 463 static int file_size(const char *); // what is the byte size of "fn"464 #if !ENABLE_FEATURE_VI_READONLY465 #define file_insert(fn, p, update_ro_status) file_insert(fn, p)466 #endif467 544 // file_insert might reallocate text[]! 468 545 static int file_insert(const char *, char *, int); … … 479 556 static void status_line(const char *, ...); // print to status buf 480 557 static void status_line_bold(const char *, ...); 558 static void status_line_bold_errno(const char *fn); 481 559 static void not_implemented(const char *); // display "Not implemented" message 482 560 static int format_edit_status(void); // format file status on status line … … 485 563 static void refresh(int); // update the terminal from screen[] 486 564 487 static void Indicate_Error(void); // use flash or beep to indicate error 488 #define indicate_error(c) Indicate_Error() 565 static void indicate_error(void); // use flash or beep to indicate error 489 566 static void Hit_Return(void); 490 567 … … 495 572 static char *get_one_address(char *, int *); // get colon addr, if present 496 573 static char *get_address(char *, int *, int *); // get two colon addrs, if present 574 #endif 497 575 static void colon(char *); // execute the "colon" mode cmds 498 #endif499 576 #if ENABLE_FEATURE_VI_USE_SIGNALS 500 577 static void winch_sig(int); // catch window size changes … … 514 591 // might reallocate text[]! use p += string_insert(p, ...), 515 592 // and be careful to not use pointers into potentially freed text[]! 516 static uintptr_t string_insert(char *, const char *); // insert the string at 'p' 593 # if !ENABLE_FEATURE_VI_UNDO 594 #define string_insert(a,b,c) string_insert(a,b) 595 # endif 596 static uintptr_t string_insert(char *, const char *, int); // insert the string at 'p' 517 597 #endif 518 598 #if ENABLE_FEATURE_VI_YANKMARK … … 521 601 static void check_context(char); // remember context for '' command 522 602 #endif 603 #if ENABLE_FEATURE_VI_UNDO 604 static void flush_undo_data(void); 605 static void undo_push(char *, unsigned int, unsigned char); // Push an operation on the undo stack 606 static void undo_pop(void); // Undo the last operation 607 # if ENABLE_FEATURE_VI_UNDO_QUEUE 608 static void undo_queue_commit(void); // Flush any queued objects to the undo stack 609 # else 610 # define undo_queue_commit() ((void)0) 611 # endif 612 #else 613 #define flush_undo_data() ((void)0) 614 #define undo_queue_commit() ((void)0) 615 #endif 616 523 617 #if ENABLE_FEATURE_VI_CRASHME 524 618 static void crash_dummy(); … … 527 621 #endif 528 622 529 530 623 static void write1(const char *out) 531 624 { … … 539 632 540 633 INIT_G(); 634 635 #if ENABLE_FEATURE_VI_UNDO 636 /* undo_stack_tail = NULL; - already is */ 637 #if ENABLE_FEATURE_VI_UNDO_QUEUE 638 undo_queue_state = UNDO_EMPTY; 639 /* undo_q = 0; - already is */ 640 #endif 641 #endif 541 642 542 643 #if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME … … 618 719 { 619 720 int rc; 620 int size = file_size(fn); // file size. -1 means does not exist. 721 722 flush_undo_data(); 723 modified_count = 0; 724 last_modified_count = -1; 725 #if ENABLE_FEATURE_VI_YANKMARK 726 /* init the marks */ 727 memset(mark, 0, sizeof(mark)); 728 #endif 621 729 622 730 /* allocate/reallocate text buffer */ 623 731 free(text); 624 text_size = size +10240;732 text_size = 10240; 625 733 screenbegin = dot = end = text = xzalloc(text_size); 626 734 … … 629 737 current_filename = xstrdup(fn); 630 738 } 631 if (size < 0) { 632 // file dont exist. Start empty buf with dummy line 633 char_insert(text, '\n'); 634 rc = 0; 635 } else { 636 rc = file_insert(fn, text, 1); 637 } 638 file_modified = 0; 639 last_file_modified = -1; 640 #if ENABLE_FEATURE_VI_YANKMARK 641 /* init the marks. */ 642 memset(mark, 0, sizeof(mark)); 643 #endif 739 rc = file_insert(fn, text, 1); 740 if (rc < 0) { 741 // file doesnt exist. Start empty buf with dummy line 742 char_insert(text, '\n', NO_UNDO); 743 } 644 744 return rc; 645 745 } … … 756 856 } else { 757 857 crashme = 0; 758 string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n" ); // insert the string858 string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n", NO_UNDO); // insert the string 759 859 dot = text; 760 860 refresh(FALSE); … … 909 1009 #endif 910 1010 1011 #endif /* FEATURE_VI_COLON */ 1012 911 1013 // buf must be no longer than MAX_INPUT_LEN! 912 1014 static void colon(char *buf) 913 1015 { 1016 #if !ENABLE_FEATURE_VI_COLON 1017 /* Simple ":cmd" handler with minimal set of commands */ 1018 char *p = buf; 1019 int cnt; 1020 1021 if (*p == ':') 1022 p++; 1023 cnt = strlen(p); 1024 if (cnt == 0) 1025 return; 1026 if (strncmp(p, "quit", cnt) == 0 1027 || strncmp(p, "q!", cnt) == 0 1028 ) { 1029 if (modified_count && p[1] != '!') { 1030 status_line_bold("No write since last change (:%s! overrides)", p); 1031 } else { 1032 editing = 0; 1033 } 1034 return; 1035 } 1036 if (strncmp(p, "write", cnt) == 0 1037 || strncmp(p, "wq", cnt) == 0 1038 || strncmp(p, "wn", cnt) == 0 1039 || (p[0] == 'x' && !p[1]) 1040 ) { 1041 cnt = file_write(current_filename, text, end - 1); 1042 if (cnt < 0) { 1043 if (cnt == -1) 1044 status_line_bold("Write error: %s", strerror(errno)); 1045 } else { 1046 modified_count = 0; 1047 last_modified_count = -1; 1048 status_line("'%s' %dL, %dC", 1049 current_filename, 1050 count_lines(text, end - 1), cnt 1051 ); 1052 if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n' 1053 || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N' 1054 ) { 1055 editing = 0; 1056 } 1057 } 1058 return; 1059 } 1060 if (strncmp(p, "file", cnt) == 0) { 1061 last_status_cksum = 0; // force status update 1062 return; 1063 } 1064 if (sscanf(p, "%d", &cnt) > 0) { 1065 dot = find_line(cnt); 1066 dot_skip_over_ws(); 1067 return; 1068 } 1069 not_implemented(p); 1070 #else 1071 914 1072 char c, *orig_buf, *buf1, *q, *r; 915 1073 char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN]; 916 int i, l, li, ch,b, e;917 int useforce , forced = FALSE;1074 int i, l, li, b, e; 1075 int useforce; 918 1076 919 1077 // :3154 // if (-e line 3154) goto it else stay put … … 937 1095 buf++; // move past the ':' 938 1096 939 li = ch =i = 0;1097 li = i = 0; 940 1098 b = e = -1; 941 1099 q = text; // assume 1,$ for the range … … 1015 1173 r = end_line(dot); 1016 1174 } 1017 dot = yank_delete(q, r, 1, YANKDEL ); // save, then delete lines1175 dot = yank_delete(q, r, 1, YANKDEL, ALLOW_UNDO); // save, then delete lines 1018 1176 dot_skip_over_ws(); 1019 1177 } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file 1178 int size; 1179 1020 1180 // don't edit, if the current file has been modified 1021 if ( file_modified&& !useforce) {1181 if (modified_count && !useforce) { 1022 1182 status_line_bold("No write since last change (:%s! overrides)", cmd); 1023 1183 goto ret; … … 1035 1195 } 1036 1196 1037 if (init_text_buffer(fn) < 0) 1038 goto ret; 1197 size = init_text_buffer(fn); 1039 1198 1040 1199 #if ENABLE_FEATURE_VI_YANKMARK … … 1050 1209 // how many lines in text[]? 1051 1210 li = count_lines(text, end - 1); 1052 status_line(" \"%s\"%s"1211 status_line("'%s'%s" 1053 1212 IF_FEATURE_VI_READONLY("%s") 1054 " %dL, %dC", current_filename, 1055 (file_size(fn) < 0 ? " [New file]" : ""), 1213 " %dL, %dC", 1214 current_filename, 1215 (size < 0 ? " [New file]" : ""), 1056 1216 IF_FEATURE_VI_READONLY( 1057 1217 ((readonly_mode) ? " [Readonly]" : ""), 1058 1218 ) 1059 li, ch); 1219 li, (int)(end - text) 1220 ); 1060 1221 } else if (strncmp(cmd, "file", i) == 0) { // what File is this 1061 1222 if (b != -1 || e != -1) { … … 1122 1283 } 1123 1284 // don't exit if the file been modified 1124 if ( file_modified) {1285 if (modified_count) { 1125 1286 status_line_bold("No write since last change (:%s! overrides)", cmd); 1126 1287 goto ret; … … 1146 1307 editing = 0; 1147 1308 } else if (strncmp(cmd, "read", i) == 0) { // read file into text[] 1309 int size; 1310 1148 1311 fn = args; 1149 1312 if (!fn[0]) { … … 1155 1318 } 1156 1319 // read after current line- unless user said ":0r foo" 1157 if (b != 0) 1320 if (b != 0) { 1158 1321 q = next_line(q); 1322 // read after last line 1323 if (q == end-1) 1324 ++q; 1325 } 1159 1326 { // dance around potentially-reallocated text[] 1160 1327 uintptr_t ofs = q - text; 1161 ch= file_insert(fn, q, 0);1328 size = file_insert(fn, q, 0); 1162 1329 q = text + ofs; 1163 1330 } 1164 if ( ch< 0)1331 if (size < 0) 1165 1332 goto ret; // nothing was inserted 1166 1333 // how many lines in text[]? 1167 li = count_lines(q, q + ch- 1);1168 status_line(" \"%s\""1334 li = count_lines(q, q + size - 1); 1335 status_line("'%s'" 1169 1336 IF_FEATURE_VI_READONLY("%s") 1170 " %dL, %dC", fn, 1337 " %dL, %dC", 1338 fn, 1171 1339 IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),) 1172 li, ch); 1173 if (ch > 0) { 1340 li, size 1341 ); 1342 if (size > 0) { 1174 1343 // if the insert is before "dot" then we need to update 1175 1344 if (q <= dot) 1176 dot += ch; 1177 /*file_modified++; - done by file_insert */ 1345 dot += size; 1178 1346 } 1179 1347 } else if (strncmp(cmd, "rewind", i) == 0) { // rewind cmd line args 1180 if ( file_modified&& !useforce) {1348 if (modified_count && !useforce) { 1181 1349 status_line_bold("No write since last change (:%s! overrides)", cmd); 1182 1350 } else { … … 1235 1403 size_t len_F, len_R; 1236 1404 int gflag; // global replace flag 1405 #if ENABLE_FEATURE_VI_UNDO 1406 int dont_chain_first_item = ALLOW_UNDO; 1407 #endif 1237 1408 1238 1409 // F points to the "find" pattern … … 1269 1440 uintptr_t bias; 1270 1441 // we found the "find" pattern - delete it 1271 text_hole_delete(found, found + len_F - 1); 1272 // inset the "replace" patern 1273 bias = string_insert(found, R); // insert the string 1442 // For undo support, the first item should not be chained 1443 text_hole_delete(found, found + len_F - 1, dont_chain_first_item); 1444 #if ENABLE_FEATURE_VI_UNDO 1445 dont_chain_first_item = ALLOW_UNDO_CHAIN; 1446 #endif 1447 // insert the "replace" patern 1448 bias = string_insert(found, R, ALLOW_UNDO_CHAIN); 1274 1449 found += bias; 1275 1450 ls += bias; … … 1293 1468 || (cmd[0] == 'x' && !cmd[1]) 1294 1469 ) { 1470 int size; 1471 //int forced = FALSE; 1472 1295 1473 // is there a file name to write to? 1296 1474 if (args[0]) { … … 1299 1477 #if ENABLE_FEATURE_VI_READONLY 1300 1478 if (readonly_mode && !useforce) { 1301 status_line_bold(" \"%s\" Fileis read only", fn);1479 status_line_bold("'%s' is read only", fn); 1302 1480 goto ret; 1303 1481 } … … 1305 1483 // how many lines in text[]? 1306 1484 li = count_lines(q, r); 1307 ch = r - q + 1; 1308 // see if file exists- if not, its just a new file request 1309 if (useforce) { 1485 size = r - q + 1; 1486 //if (useforce) { 1310 1487 // if "fn" is not write-able, chmod u+w 1311 1488 // sprintf(syscmd, "chmod u+w %s", fn); 1312 1489 // system(syscmd); 1313 forced = TRUE;1314 }1490 // forced = TRUE; 1491 //} 1315 1492 l = file_write(fn, q, r); 1316 if (useforce && forced) {1493 //if (useforce && forced) { 1317 1494 // chmod u-w 1318 1495 // sprintf(syscmd, "chmod u-w %s", fn); 1319 1496 // system(syscmd); 1320 forced = FALSE;1321 }1497 // forced = FALSE; 1498 //} 1322 1499 if (l < 0) { 1323 1500 if (l == -1) 1324 status_line_bold ("\"%s\" %s", fn, strerror(errno));1501 status_line_bold_errno(fn); 1325 1502 } else { 1326 status_line(" \"%s\"%dL, %dC", fn, li, l);1327 if (q == text && r == end - 1 && l == ch) {1328 file_modified= 0;1329 last_ file_modified= -1;1503 status_line("'%s' %dL, %dC", fn, li, l); 1504 if (q == text && r == end - 1 && l == size) { 1505 modified_count = 0; 1506 last_modified_count = -1; 1330 1507 } 1331 1508 if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n' 1332 1509 || cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N' 1333 1510 ) 1334 && l == ch1511 && l == size 1335 1512 ) { 1336 1513 editing = 0; … … 1359 1536 status_line(":s expression missing delimiters"); 1360 1537 #endif 1361 }1362 1363 1538 #endif /* FEATURE_VI_COLON */ 1539 } 1364 1540 1365 1541 static void Hit_Return(void) … … 1508 1684 static char *prev_line(char *p) // return pointer first char prev line 1509 1685 { 1510 p = begin_line(p); // goto begin ing of cur line1686 p = begin_line(p); // goto beginning of cur line 1511 1687 if (p > text && p[-1] == '\n') 1512 1688 p--; // step to prev line 1513 p = begin_line(p); // goto begin ing of prev line1689 p = begin_line(p); // goto beginning of prev line 1514 1690 return p; 1515 1691 } … … 1559 1735 } 1560 1736 1561 static char *find_line(int li) // find begin ing of line #li1737 static char *find_line(int li) // find beginning of line #li 1562 1738 { 1563 1739 char *q; … … 1572 1748 static void dot_left(void) 1573 1749 { 1750 undo_queue_commit(); 1574 1751 if (dot > text && dot[-1] != '\n') 1575 1752 dot--; … … 1578 1755 static void dot_right(void) 1579 1756 { 1757 undo_queue_commit(); 1580 1758 if (dot < end - 1 && *dot != '\n') 1581 1759 dot++; … … 1584 1762 static void dot_begin(void) 1585 1763 { 1764 undo_queue_commit(); 1586 1765 dot = begin_line(dot); // return pointer to first char cur line 1587 1766 } … … 1589 1768 static void dot_end(void) 1590 1769 { 1770 undo_queue_commit(); 1591 1771 dot = end_line(dot); // return pointer to last char cur line 1592 1772 } … … 1614 1794 static void dot_next(void) 1615 1795 { 1796 undo_queue_commit(); 1616 1797 dot = next_line(dot); 1617 1798 } … … 1619 1800 static void dot_prev(void) 1620 1801 { 1802 undo_queue_commit(); 1621 1803 dot = prev_line(dot); 1622 1804 } … … 1626 1808 char *q; 1627 1809 1810 undo_queue_commit(); 1628 1811 for (; cnt > 0; cnt--) { 1629 1812 if (dir < 0) { … … 1653 1836 } 1654 1837 1655 static void dot_delete(void) // delete the char at 'dot'1656 {1657 text_hole_delete(dot, dot);1658 }1659 1660 1838 static char *bound_dot(char *p) // make sure text[0] <= P < "end" 1661 1839 { 1662 1840 if (p >= end && end > text) { 1663 1841 p = end - 1; 1664 indicate_error( '1');1842 indicate_error(); 1665 1843 } 1666 1844 if (p < text) { 1667 1845 p = text; 1668 indicate_error( '2');1846 indicate_error(); 1669 1847 } 1670 1848 return p; … … 1707 1885 static char *char_search(char *p, const char *pat, int dir, int range) 1708 1886 { 1887 struct re_pattern_buffer preg; 1888 const char *err; 1709 1889 char *q; 1710 struct re_pattern_buffer preg;1711 1890 int i; 1712 1891 int size; 1713 1892 1714 1893 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED; 1715 preg.translate = 0; 1716 preg.fastmap = 0; 1717 preg.buffer = 0; 1718 preg.allocated = 0; 1894 if (ignorecase) 1895 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED | RE_ICASE; 1896 1897 memset(&preg, 0, sizeof(preg)); 1898 err = re_compile_pattern(pat, strlen(pat), &preg); 1899 if (err != NULL) { 1900 status_line_bold("bad search pattern '%s': %s", pat, err); 1901 return p; 1902 } 1719 1903 1720 1904 // assume a LIMITED forward search 1721 q = next_line(p);1722 q = end_line(q);1723 1905 q = end - 1; 1724 if (dir == BACK) { 1725 q = prev_line(p); 1906 if (dir == BACK) 1726 1907 q = text; 1727 }1728 // count the number of chars to search over, forward or backward1729 size = q - p;1730 if (size < 0)1731 size = p - q;1732 1908 // RANGE could be negative if we are searching backwards 1733 1909 range = q - p; 1734 1735 q = (char *)re_compile_pattern(pat, strlen(pat), (struct re_pattern_buffer *)&preg);1736 if (q != 0) {1737 // The pattern was not compiled1738 status_line_bold("bad search pattern: \"%s\": %s", pat, q);1739 i = 0; // return p if pattern not compiled1740 goto cs1;1741 }1742 1743 1910 q = p; 1911 size = range; 1744 1912 if (range < 0) { 1913 size = -size; 1745 1914 q = p - size; 1746 1915 if (q < text) … … 1748 1917 } 1749 1918 // search for the compiled pattern, preg, in p[] 1750 // range < 0 -search backward1751 // range > 0 -search forward1919 // range < 0: search backward 1920 // range > 0: search forward 1752 1921 // 0 < start < size 1753 // re_search() < 0 not found or error 1754 // re_search() > 0 index of found pattern 1755 // struct pattern char int int int struct reg 1756 // re_search (*pattern_buffer, *string, size, start, range, *regs) 1757 i = re_search(&preg, q, size, 0, range, 0); 1758 if (i == -1) { 1759 p = 0; 1760 i = 0; // return NULL if pattern not found 1761 } 1762 cs1: 1763 if (dir == FORWARD) { 1922 // re_search() < 0: not found or error 1923 // re_search() >= 0: index of found pattern 1924 // struct pattern char int int int struct reg 1925 // re_search(*pattern_buffer, *string, size, start, range, *regs) 1926 i = re_search(&preg, q, size, /*start:*/ 0, range, /*struct re_registers*:*/ NULL); 1927 regfree(&preg); 1928 if (i < 0) 1929 return NULL; 1930 if (dir == FORWARD) 1764 1931 p = p + i; 1765 } else {1932 else 1766 1933 p = p - i; 1767 }1768 1934 return p; 1769 1935 } … … 1790 1956 len = strlen(pat); 1791 1957 if (dir == FORWARD) { 1792 stop = end - 1; // assume range is p -end-11958 stop = end - 1; // assume range is p..end-1 1793 1959 if (range == LIMITED) 1794 1960 stop = next_line(p); // range is to next line … … 1799 1965 } 1800 1966 } else if (dir == BACK) { 1801 stop = text; // assume range is text -p1967 stop = text; // assume range is text..p 1802 1968 if (range == LIMITED) 1803 1969 stop = prev_line(p); // range is to prev line … … 1816 1982 #endif /* FEATURE_VI_SEARCH */ 1817 1983 1818 static char *char_insert(char *p, char c ) // insert the char c at 'p'1984 static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' 1819 1985 { 1820 1986 if (c == 22) { // Is this an ctrl-V? … … 1823 1989 c = get_one_char(); 1824 1990 *p = c; 1991 #if ENABLE_FEATURE_VI_UNDO 1992 switch (undo) { 1993 case ALLOW_UNDO: 1994 undo_push(p, 1, UNDO_INS); 1995 break; 1996 case ALLOW_UNDO_CHAIN: 1997 undo_push(p, 1, UNDO_INS_CHAIN); 1998 break; 1999 # if ENABLE_FEATURE_VI_UNDO_QUEUE 2000 case ALLOW_UNDO_QUEUED: 2001 undo_push(p, 1, UNDO_INS_QUEUED); 2002 break; 2003 # endif 2004 } 2005 #else 2006 modified_count++; 2007 #endif /* ENABLE_FEATURE_VI_UNDO */ 1825 2008 p++; 1826 file_modified++;1827 2009 } else if (c == 27) { // Is this an ESC? 1828 2010 cmd_mode = 0; 2011 undo_queue_commit(); 1829 2012 cmdcnt = 0; 1830 2013 end_cmd_q(); // stop adding to q … … 1834 2017 } 1835 2018 } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS 1836 // 123456789 1837 if ((p[-1] != '\n') && (dot>text)) { 2019 if (p > text) { 1838 2020 p--; 1839 p = text_hole_delete(p, p ); // shrink buffer 1 char2021 p = text_hole_delete(p, p, ALLOW_UNDO_QUEUED); // shrink buffer 1 char 1840 2022 } 1841 2023 } else { 1842 #if ENABLE_FEATURE_VI_SETOPTS1843 2024 // insert a char into text[] 1844 char *sp; // "save p"1845 #endif1846 1847 2025 if (c == 13) 1848 2026 c = '\n'; // translate \r to \n 1849 #if ENABLE_FEATURE_VI_SETOPTS 1850 sp = p; // remember addr of insert 1851 #endif 2027 #if ENABLE_FEATURE_VI_UNDO 2028 # if ENABLE_FEATURE_VI_UNDO_QUEUE 2029 if (c == '\n') 2030 undo_queue_commit(); 2031 # endif 2032 switch (undo) { 2033 case ALLOW_UNDO: 2034 undo_push(p, 1, UNDO_INS); 2035 break; 2036 case ALLOW_UNDO_CHAIN: 2037 undo_push(p, 1, UNDO_INS_CHAIN); 2038 break; 2039 # if ENABLE_FEATURE_VI_UNDO_QUEUE 2040 case ALLOW_UNDO_QUEUED: 2041 undo_push(p, 1, UNDO_INS_QUEUED); 2042 break; 2043 # endif 2044 } 2045 #else 2046 modified_count++; 2047 #endif /* ENABLE_FEATURE_VI_UNDO */ 1852 2048 p += 1 + stupid_insert(p, c); // insert the char 1853 2049 #if ENABLE_FEATURE_VI_SETOPTS 1854 if (showmatch && strchr(")]}", *sp) != NULL) {1855 showmatching( sp);2050 if (showmatch && strchr(")]}", c) != NULL) { 2051 showmatching(p - 1); 1856 2052 } 1857 2053 if (autoindent && c == '\n') { // auto indent the new line … … 1865 2061 p += bias; 1866 2062 q += bias; 2063 #if ENABLE_FEATURE_VI_UNDO 2064 undo_push(p, len, UNDO_INS); 2065 #endif 1867 2066 memcpy(p, q, len); 1868 2067 p += len; … … 1882 2081 p += bias; 1883 2082 *p = c; 1884 //file_modified++; - done by text_hole_make()1885 2083 return bias; 1886 2084 } … … 2011 2209 2012 2210 // find matching char of pair () [] {} 2211 // will crash if c is not one of these 2013 2212 static char *find_pair(char *p, const char c) 2014 2213 { 2015 char match, *q; 2214 const char *braces = "()[]{}"; 2215 char match; 2016 2216 int dir, level; 2017 2217 2018 match = ')'; 2218 dir = strchr(braces, c) - braces; 2219 dir ^= 1; 2220 match = braces[dir]; 2221 dir = ((dir & 1) << 1) - 1; /* 1 for ([{, -1 for )\} */ 2222 2223 // look for match, count levels of pairs (( )) 2019 2224 level = 1; 2020 dir = 1; // assume forward 2021 switch (c) { 2022 case '(': match = ')'; break; 2023 case '[': match = ']'; break; 2024 case '{': match = '}'; break; 2025 case ')': match = '('; dir = -1; break; 2026 case ']': match = '['; dir = -1; break; 2027 case '}': match = '{'; dir = -1; break; 2028 } 2029 for (q = p + dir; text <= q && q < end; q += dir) { 2030 // look for match, count levels of pairs (( )) 2031 if (*q == c) 2225 for (;;) { 2226 p += dir; 2227 if (p < text || p >= end) 2228 return NULL; 2229 if (*p == c) 2032 2230 level++; // increase pair levels 2033 if (* q == match)2231 if (*p == match) { 2034 2232 level--; // reduce pair level 2035 if (level == 0) 2036 break; // found matching pair 2037 } 2038 if (level != 0) 2039 q = NULL; // indicate no match 2040 return q; 2233 if (level == 0) 2234 return p; // found matching pair 2235 } 2236 } 2041 2237 } 2042 2238 … … 2050 2246 q = find_pair(p, *p); // get loc of matching char 2051 2247 if (q == NULL) { 2052 indicate_error( '3'); // no matching char2248 indicate_error(); // no matching char 2053 2249 } else { 2054 2250 // "q" now points to matching pair … … 2062 2258 } 2063 2259 #endif /* FEATURE_VI_SETOPTS */ 2260 2261 #if ENABLE_FEATURE_VI_UNDO 2262 static void flush_undo_data(void) 2263 { 2264 struct undo_object *undo_entry; 2265 2266 while (undo_stack_tail) { 2267 undo_entry = undo_stack_tail; 2268 undo_stack_tail = undo_entry->prev; 2269 free(undo_entry); 2270 } 2271 } 2272 2273 // Undo functions and hooks added by Jody Bruchon (jody@jodybruchon.com) 2274 static void undo_push(char *src, unsigned int length, uint8_t u_type) // Add to the undo stack 2275 { 2276 struct undo_object *undo_entry; 2277 2278 // "u_type" values 2279 // UNDO_INS: insertion, undo will remove from buffer 2280 // UNDO_DEL: deleted text, undo will restore to buffer 2281 // UNDO_{INS,DEL}_CHAIN: Same as above but also calls undo_pop() when complete 2282 // The CHAIN operations are for handling multiple operations that the user 2283 // performs with a single action, i.e. REPLACE mode or find-and-replace commands 2284 // UNDO_{INS,DEL}_QUEUED: If queuing feature is enabled, allow use of the queue 2285 // for the INS/DEL operation. The raw values should be equal to the values of 2286 // UNDO_{INS,DEL} ORed with UNDO_QUEUED_FLAG 2287 2288 #if ENABLE_FEATURE_VI_UNDO_QUEUE 2289 // This undo queuing functionality groups multiple character typing or backspaces 2290 // into a single large undo object. This greatly reduces calls to malloc() for 2291 // single-character operations while typing and has the side benefit of letting 2292 // an undo operation remove chunks of text rather than a single character. 2293 switch (u_type) { 2294 case UNDO_EMPTY: // Just in case this ever happens... 2295 return; 2296 case UNDO_DEL_QUEUED: 2297 if (length != 1) 2298 return; // Only queue single characters 2299 switch (undo_queue_state) { 2300 case UNDO_EMPTY: 2301 undo_queue_state = UNDO_DEL; 2302 case UNDO_DEL: 2303 undo_queue_spos = src; 2304 undo_q++; 2305 undo_queue[CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q] = *src; 2306 // If queue is full, dump it into an object 2307 if (undo_q == CONFIG_FEATURE_VI_UNDO_QUEUE_MAX) 2308 undo_queue_commit(); 2309 return; 2310 case UNDO_INS: 2311 // Switch from storing inserted text to deleted text 2312 undo_queue_commit(); 2313 undo_push(src, length, UNDO_DEL_QUEUED); 2314 return; 2315 } 2316 break; 2317 case UNDO_INS_QUEUED: 2318 if (length != 1) 2319 return; 2320 switch (undo_queue_state) { 2321 case UNDO_EMPTY: 2322 undo_queue_state = UNDO_INS; 2323 undo_queue_spos = src; 2324 case UNDO_INS: 2325 undo_q++; // Don't need to save any data for insertions 2326 if (undo_q == CONFIG_FEATURE_VI_UNDO_QUEUE_MAX) 2327 undo_queue_commit(); 2328 return; 2329 case UNDO_DEL: 2330 // Switch from storing deleted text to inserted text 2331 undo_queue_commit(); 2332 undo_push(src, length, UNDO_INS_QUEUED); 2333 return; 2334 } 2335 break; 2336 } 2337 #else 2338 // If undo queuing is disabled, ignore the queuing flag entirely 2339 u_type = u_type & ~UNDO_QUEUED_FLAG; 2340 #endif 2341 2342 // Allocate a new undo object 2343 if (u_type == UNDO_DEL || u_type == UNDO_DEL_CHAIN) { 2344 // For UNDO_DEL objects, save deleted text 2345 if ((src + length) == end) 2346 length--; 2347 // If this deletion empties text[], strip the newline. When the buffer becomes 2348 // zero-length, a newline is added back, which requires this to compensate. 2349 undo_entry = xzalloc(offsetof(struct undo_object, undo_text) + length); 2350 memcpy(undo_entry->undo_text, src, length); 2351 } else { 2352 undo_entry = xzalloc(sizeof(*undo_entry)); 2353 } 2354 undo_entry->length = length; 2355 #if ENABLE_FEATURE_VI_UNDO_QUEUE 2356 if ((u_type & UNDO_USE_SPOS) != 0) { 2357 undo_entry->start = undo_queue_spos - text; // use start position from queue 2358 } else { 2359 undo_entry->start = src - text; // use offset from start of text buffer 2360 } 2361 u_type = (u_type & ~UNDO_USE_SPOS); 2362 #else 2363 undo_entry->start = src - text; 2364 #endif 2365 undo_entry->u_type = u_type; 2366 2367 // Push it on undo stack 2368 undo_entry->prev = undo_stack_tail; 2369 undo_stack_tail = undo_entry; 2370 modified_count++; 2371 } 2372 2373 static void undo_pop(void) // Undo the last operation 2374 { 2375 int repeat; 2376 char *u_start, *u_end; 2377 struct undo_object *undo_entry; 2378 2379 // Commit pending undo queue before popping (should be unnecessary) 2380 undo_queue_commit(); 2381 2382 undo_entry = undo_stack_tail; 2383 // Check for an empty undo stack 2384 if (!undo_entry) { 2385 status_line("Already at oldest change"); 2386 return; 2387 } 2388 2389 switch (undo_entry->u_type) { 2390 case UNDO_DEL: 2391 case UNDO_DEL_CHAIN: 2392 // make hole and put in text that was deleted; deallocate text 2393 u_start = text + undo_entry->start; 2394 text_hole_make(u_start, undo_entry->length); 2395 memcpy(u_start, undo_entry->undo_text, undo_entry->length); 2396 status_line("Undo [%d] %s %d chars at position %d", 2397 modified_count, "restored", 2398 undo_entry->length, undo_entry->start 2399 ); 2400 break; 2401 case UNDO_INS: 2402 case UNDO_INS_CHAIN: 2403 // delete what was inserted 2404 u_start = undo_entry->start + text; 2405 u_end = u_start - 1 + undo_entry->length; 2406 text_hole_delete(u_start, u_end, NO_UNDO); 2407 status_line("Undo [%d] %s %d chars at position %d", 2408 modified_count, "deleted", 2409 undo_entry->length, undo_entry->start 2410 ); 2411 break; 2412 } 2413 repeat = 0; 2414 switch (undo_entry->u_type) { 2415 // If this is the end of a chain, lower modification count and refresh display 2416 case UNDO_DEL: 2417 case UNDO_INS: 2418 dot = (text + undo_entry->start); 2419 refresh(FALSE); 2420 break; 2421 case UNDO_DEL_CHAIN: 2422 case UNDO_INS_CHAIN: 2423 repeat = 1; 2424 break; 2425 } 2426 // Deallocate the undo object we just processed 2427 undo_stack_tail = undo_entry->prev; 2428 free(undo_entry); 2429 modified_count--; 2430 // For chained operations, continue popping all the way down the chain. 2431 if (repeat) { 2432 undo_pop(); // Follow the undo chain if one exists 2433 } 2434 } 2435 2436 #if ENABLE_FEATURE_VI_UNDO_QUEUE 2437 static void undo_queue_commit(void) // Flush any queued objects to the undo stack 2438 { 2439 // Pushes the queue object onto the undo stack 2440 if (undo_q > 0) { 2441 // Deleted character undo events grow from the end 2442 undo_push(undo_queue + CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q, 2443 undo_q, 2444 (undo_queue_state | UNDO_USE_SPOS) 2445 ); 2446 undo_queue_state = UNDO_EMPTY; 2447 undo_q = 0; 2448 } 2449 } 2450 #endif 2451 2452 #endif /* ENABLE_FEATURE_VI_UNDO */ 2064 2453 2065 2454 // open a hole in text[] … … 2094 2483 memmove(p + size, p, end - size - p); 2095 2484 memset(p, ' ', size); // clear new hole 2096 file_modified++;2097 2485 return bias; 2098 2486 } 2099 2487 2100 2488 // close a hole in text[] 2101 static char *text_hole_delete(char *p, char *q) // delete "p" through "q", inclusive 2489 // "undo" value indicates if this operation should be undo-able 2490 static char *text_hole_delete(char *p, char *q, int undo) // delete "p" through "q", inclusive 2102 2491 { 2103 2492 char *src, *dest; … … 2114 2503 hole_size = q - p + 1; 2115 2504 cnt = end - src; 2505 #if ENABLE_FEATURE_VI_UNDO 2506 switch (undo) { 2507 case NO_UNDO: 2508 break; 2509 case ALLOW_UNDO: 2510 undo_push(p, hole_size, UNDO_DEL); 2511 break; 2512 case ALLOW_UNDO_CHAIN: 2513 undo_push(p, hole_size, UNDO_DEL_CHAIN); 2514 break; 2515 # if ENABLE_FEATURE_VI_UNDO_QUEUE 2516 case ALLOW_UNDO_QUEUED: 2517 undo_push(p, hole_size, UNDO_DEL_QUEUED); 2518 break; 2519 # endif 2520 } 2521 modified_count--; 2522 #endif 2116 2523 if (src < text || src > end) 2117 2524 goto thd0; 2118 2525 if (dest < text || dest >= end) 2119 2526 goto thd0; 2527 modified_count++; 2120 2528 if (src >= end) 2121 2529 goto thd_atend; // just delete the end of the buffer … … 2127 2535 if (end <= text) 2128 2536 dest = end = text; // keep pointers valid 2129 file_modified++;2130 2537 thd0: 2131 2538 return dest; … … 2135 2542 // if dist <= 0, do not include, or go past, a NewLine 2136 2543 // 2137 static char *yank_delete(char *start, char *stop, int dist, int yf )2544 static char *yank_delete(char *start, char *stop, int dist, int yf, int undo) 2138 2545 { 2139 2546 char *p; … … 2164 2571 #endif 2165 2572 if (yf == YANKDEL) { 2166 p = text_hole_delete(start, stop );2573 p = text_hole_delete(start, stop, undo); 2167 2574 } // delete lines 2168 2575 return p; … … 2230 2637 // might reallocate text[]! use p += string_insert(p, ...), 2231 2638 // and be careful to not use pointers into potentially freed text[]! 2232 static uintptr_t string_insert(char *p, const char *s ) // insert the string at 'p'2639 static uintptr_t string_insert(char *p, const char *s, int undo) // insert the string at 'p' 2233 2640 { 2234 2641 uintptr_t bias; … … 2236 2643 2237 2644 i = strlen(s); 2645 #if ENABLE_FEATURE_VI_UNDO 2646 switch (undo) { 2647 case ALLOW_UNDO: 2648 undo_push(p, i, UNDO_INS); 2649 break; 2650 case ALLOW_UNDO_CHAIN: 2651 undo_push(p, i, UNDO_INS_CHAIN); 2652 break; 2653 } 2654 #endif 2238 2655 bias = text_hole_make(p, i); 2239 2656 p += bias; … … 2387 2804 { 2388 2805 struct pollfd pfd[1]; 2806 2807 if (hund != 0) 2808 fflush_all(); 2389 2809 2390 2810 pfd[0].fd = STDIN_FILENO; … … 2484 2904 } 2485 2905 2486 static int file_size(const char *fn) // what is the byte size of "fn"2487 {2488 struct stat st_buf;2489 int cnt;2490 2491 cnt = -1;2492 if (fn && stat(fn, &st_buf) == 0) // see if file exists2493 cnt = (int) st_buf.st_size;2494 return cnt;2495 }2496 2497 2906 // might reallocate text[]! 2498 static int file_insert(const char *fn, char *p, int update_ro_status)2907 static int file_insert(const char *fn, char *p, int initial) 2499 2908 { 2500 2909 int cnt = -1; … … 2502 2911 struct stat statbuf; 2503 2912 2504 /* Validate file */ 2505 if (stat(fn, &statbuf) < 0) { 2506 status_line_bold("\"%s\" %s", fn, strerror(errno)); 2507 goto fi0; 2508 } 2509 if (!S_ISREG(statbuf.st_mode)) { 2510 // This is not a regular file 2511 status_line_bold("\"%s\" Not a regular file", fn); 2512 goto fi0; 2513 } 2514 if (p < text || p > end) { 2515 status_line_bold("Trying to insert file outside of memory"); 2516 goto fi0; 2517 } 2518 2519 // read file to buffer 2913 if (p < text) 2914 p = text; 2915 if (p > end) 2916 p = end; 2917 2520 2918 fd = open(fn, O_RDONLY); 2521 2919 if (fd < 0) { 2522 status_line_bold("\"%s\" %s", fn, strerror(errno)); 2523 goto fi0; 2524 } 2525 size = statbuf.st_size; 2920 if (!initial) 2921 status_line_bold_errno(fn); 2922 return cnt; 2923 } 2924 2925 /* Validate file */ 2926 if (fstat(fd, &statbuf) < 0) { 2927 status_line_bold_errno(fn); 2928 goto fi; 2929 } 2930 if (!S_ISREG(statbuf.st_mode)) { 2931 status_line_bold("'%s' is not a regular file", fn); 2932 goto fi; 2933 } 2934 size = (statbuf.st_size < INT_MAX ? (int)statbuf.st_size : INT_MAX); 2526 2935 p += text_hole_make(p, size); 2527 cnt = safe_read(fd, p, size);2936 cnt = full_read(fd, p, size); 2528 2937 if (cnt < 0) { 2529 status_line_bold ("\"%s\" %s", fn, strerror(errno));2530 p = text_hole_delete(p, p + size - 1 ); // un-do buffer insert2938 status_line_bold_errno(fn); 2939 p = text_hole_delete(p, p + size - 1, NO_UNDO); // un-do buffer insert 2531 2940 } else if (cnt < size) { 2532 // There was a partial read, shrink unused space text[] 2533 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert 2534 status_line_bold("can't read all of file \"%s\"", fn); 2535 } 2536 if (cnt >= size) 2537 file_modified++; 2941 // There was a partial read, shrink unused space 2942 p = text_hole_delete(p + cnt, p + size - 1, NO_UNDO); 2943 status_line_bold("can't read '%s'", fn); 2944 } 2945 fi: 2538 2946 close(fd); 2539 fi0: 2947 2540 2948 #if ENABLE_FEATURE_VI_READONLY 2541 if ( update_ro_status2949 if (initial 2542 2950 && ((access(fn, W_OK) < 0) || 2543 2951 /* root will always have access() … … 2572 2980 if (charcnt == cnt) { 2573 2981 // good write 2574 // file_modified= FALSE;2982 //modified_count = FALSE; 2575 2983 } else { 2576 2984 charcnt = 0; … … 2645 3053 } 2646 3054 2647 static void Indicate_Error(void)3055 static void indicate_error(void) 2648 3056 { 2649 3057 #if ENABLE_FEATURE_VI_CRASHME … … 2718 3126 } 2719 3127 3128 static void status_line_bold_errno(const char *fn) 3129 { 3130 status_line_bold("'%s' %s", fn, strerror(errno)); 3131 } 3132 2720 3133 // format status buffer 2721 3134 static void status_line(const char *format, ...) … … 2789 3202 int cur, percent, ret, trunc_at; 2790 3203 2791 // file_modifiedis now a counter rather than a flag. this3204 // modified_count is now a counter rather than a flag. this 2792 3205 // helps reduce the amount of line counting we need to do. 2793 3206 // (this will cause a mis-reporting of modified status … … 2799 3212 cur = count_lines(text, dot); 2800 3213 2801 // reduce counting -- the total lines can't have 2802 // changed if we haven't done any edits. 2803 if (file_modified != last_file_modified) { 3214 // count_lines() is expensive. 3215 // Call it only if something was changed since last time 3216 // we were here: 3217 if (modified_count != last_modified_count) { 2804 3218 tot = cur + count_lines(dot, end - 1) - 1; 2805 last_ file_modified = file_modified;3219 last_modified_count = modified_count; 2806 3220 } 2807 3221 … … 2830 3244 (readonly_mode ? " [Readonly]" : ""), 2831 3245 #endif 2832 ( file_modified? " [Modified]" : ""),3246 (modified_count ? " [Modified]" : ""), 2833 3247 cur, tot, percent); 2834 3248 … … 2943 3357 } 2944 3358 2945 // see if there are any changes between vi tual screen and out_buf3359 // see if there are any changes between virtual screen and out_buf 2946 3360 changed = FALSE; // assume no change 2947 3361 cs = 0; … … 2980 3394 if (ce > columns - 1) ce = columns - 1; 2981 3395 if (cs > ce) { cs = 0; ce = columns - 1; } 2982 // is there a change between vi tual screen and out_buf3396 // is there a change between virtual screen and out_buf 2983 3397 if (changed) { 2984 3398 // copy changed part of buffer to virtual screen … … 3055 3469 // don't Replace past E-o-l 3056 3470 cmd_mode = 1; // convert to insert 3471 undo_queue_commit(); 3057 3472 } else { 3058 3473 if (1 <= c || Isprint(c)) { 3059 3474 if (c != 27) 3060 dot = yank_delete(dot, dot, 0, YANKDEL ); // delete char3061 dot = char_insert(dot, c ); // insert new char3475 dot = yank_delete(dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete char 3476 dot = char_insert(dot, c, ALLOW_UNDO_CHAIN); // insert new char 3062 3477 } 3063 3478 goto dc1; … … 3069 3484 // insert the char c at "dot" 3070 3485 if (1 <= c || Isprint(c)) { 3071 dot = char_insert(dot, c );3486 dot = char_insert(dot, c, ALLOW_UNDO_QUEUED); 3072 3487 } 3073 3488 goto dc1; … … 3115 3530 //case '_': // _- 3116 3531 //case '`': // `- 3117 //case 'u': // u- FIXME- there is no undo3118 3532 //case 'v': // v- 3119 3533 default: // unrecognized command … … 3182 3596 case 27: // esc 3183 3597 if (cmd_mode == 0) 3184 indicate_error( c);3598 indicate_error(); 3185 3599 cmd_mode = 0; // stop insrting 3600 undo_queue_commit(); 3186 3601 end_cmd_q(); 3187 3602 last_status_cksum = 0; // force status update … … 3200 3615 YDreg = c1; 3201 3616 } else { 3202 indicate_error( c);3617 indicate_error(); 3203 3618 } 3204 3619 break; … … 3218 3633 dot_skip_over_ws(); 3219 3634 } else { 3220 indicate_error( c);3635 indicate_error(); 3221 3636 } 3222 3637 break; … … 3231 3646 mark[c1] = dot; 3232 3647 } else { 3233 indicate_error( c);3648 indicate_error(); 3234 3649 } 3235 3650 break; … … 3258 3673 dot_right(); // move to right, can move to NL 3259 3674 } 3260 string_insert(dot, p ); // insert the string3675 string_insert(dot, p, ALLOW_UNDO); // insert the string 3261 3676 end_cmd_q(); // stop adding to q 3262 3677 break; … … 3265 3680 p = begin_line(dot); 3266 3681 q = end_line(dot); 3267 p = text_hole_delete(p, q ); // delete cur line3268 p += string_insert(p, reg[Ureg] ); // insert orig line3682 p = text_hole_delete(p, q, ALLOW_UNDO); // delete cur line 3683 p += string_insert(p, reg[Ureg], ALLOW_UNDO_CHAIN); // insert orig line 3269 3684 dot = p; 3270 3685 dot_skip_over_ws(); … … 3272 3687 break; 3273 3688 #endif /* FEATURE_VI_YANKMARK */ 3689 #if ENABLE_FEATURE_VI_UNDO 3690 case 'u': // u- undo last operation 3691 undo_pop(); 3692 break; 3693 #endif 3274 3694 case '$': // $- goto end of line 3275 3695 case KEYCODE_END: // Cursor Key End … … 3287 3707 p = find_pair(q, *q); 3288 3708 if (p == NULL) { 3289 indicate_error( c);3709 indicate_error(); 3290 3710 } else { 3291 3711 dot = p; … … 3295 3715 } 3296 3716 if (*q == '\n') 3297 indicate_error( c);3717 indicate_error(); 3298 3718 break; 3299 3719 case 'f': // f- forward to a user specified char … … 3424 3844 break; 3425 3845 #endif /* FEATURE_VI_SEARCH */ 3426 case '0': // 0- goto begin ing of line3846 case '0': // 0- goto beginning of line 3427 3847 case '1': // 1- 3428 3848 case '2': // 2- … … 3442 3862 case ':': // :- the colon mode commands 3443 3863 p = get_input_line(":"); // get input line- use "status line" 3444 #if ENABLE_FEATURE_VI_COLON3445 3864 colon(p); // execute the command 3446 #else3447 if (*p == ':')3448 p++; // move past the ':'3449 cnt = strlen(p);3450 if (cnt <= 0)3451 break;3452 if (strncmp(p, "quit", cnt) == 03453 || strncmp(p, "q!", cnt) == 0 // delete lines3454 ) {3455 if (file_modified && p[1] != '!') {3456 status_line_bold("No write since last change (:%s! overrides)", p);3457 } else {3458 editing = 0;3459 }3460 } else if (strncmp(p, "write", cnt) == 03461 || strncmp(p, "wq", cnt) == 03462 || strncmp(p, "wn", cnt) == 03463 || (p[0] == 'x' && !p[1])3464 ) {3465 cnt = file_write(current_filename, text, end - 1);3466 if (cnt < 0) {3467 if (cnt == -1)3468 status_line_bold("Write error: %s", strerror(errno));3469 } else {3470 file_modified = 0;3471 last_file_modified = -1;3472 status_line("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt);3473 if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'3474 || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'3475 ) {3476 editing = 0;3477 }3478 }3479 } else if (strncmp(p, "file", cnt) == 0) {3480 last_status_cksum = 0; // force status update3481 } else if (sscanf(p, "%d", &j) > 0) {3482 dot = find_line(j); // go to line # j3483 dot_skip_over_ws();3484 } else { // unrecognized cmd3485 not_implemented(p);3486 }3487 #endif /* !FEATURE_VI_COLON */3488 3865 break; 3489 3866 case '<': // <- Left shift something … … 3492 3869 c1 = get_one_char(); // get the type of thing to delete 3493 3870 find_range(&p, &q, c1); 3494 yank_delete(p, q, 1, YANKONLY ); // save copy before change3871 yank_delete(p, q, 1, YANKONLY, NO_UNDO); // save copy before change 3495 3872 p = begin_line(p); 3496 3873 q = end_line(q); … … 3501 3878 if (*p == '\t') { 3502 3879 // shrink buffer 1 char 3503 text_hole_delete(p, p );3880 text_hole_delete(p, p, NO_UNDO); 3504 3881 } else if (*p == ' ') { 3505 3882 // we should be calculating columns, not just SPACE 3506 3883 for (j = 0; *p == ' ' && j < tabstop; j++) { 3507 text_hole_delete(p, p );3884 text_hole_delete(p, p, NO_UNDO); 3508 3885 } 3509 3886 } 3510 3887 } else if (c == '>') { 3511 3888 // shift right -- add tab or 8 spaces 3512 char_insert(p, '\t' );3889 char_insert(p, '\t', ALLOW_UNDO); 3513 3890 } 3514 3891 } … … 3545 3922 dot = dollar_line(dot); // move to before NL 3546 3923 // copy text into a register and delete 3547 dot = yank_delete(save_dot, dot, 0, YANKDEL ); // delete to e-o-l3924 dot = yank_delete(save_dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete to e-o-l 3548 3925 if (c == 'C') 3549 3926 goto dc_i; // start inserting … … 3590 3967 dc_i: 3591 3968 cmd_mode = 1; // start inserting 3969 undo_queue_commit(); // commit queue when cmd_mode changes 3592 3970 break; 3593 3971 case 'J': // J- join current and next lines together … … 3595 3973 dot_end(); // move to NL 3596 3974 if (dot < end - 1) { // make sure not last char in text[] 3975 #if ENABLE_FEATURE_VI_UNDO 3976 undo_push(dot, 1, UNDO_DEL); 3597 3977 *dot++ = ' '; // replace NL with space 3598 file_modified++; 3978 undo_push((dot - 1), 1, UNDO_INS_CHAIN); 3979 #else 3980 *dot++ = ' '; 3981 modified_count++; 3982 #endif 3599 3983 while (isblank(*dot)) { // delete leading WS 3600 dot_delete();3984 text_hole_delete(dot, dot, ALLOW_UNDO_CHAIN); 3601 3985 } 3602 3986 } … … 3627 4011 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..." 3628 4012 dot_end(); 3629 dot = char_insert(dot, '\n' );4013 dot = char_insert(dot, '\n', ALLOW_UNDO); 3630 4014 } else { 3631 4015 dot_begin(); // 0 3632 dot = char_insert(dot, '\n' ); // i\n ESC4016 dot = char_insert(dot, '\n', ALLOW_UNDO); // i\n ESC 3633 4017 dot_prev(); // - 3634 4018 } … … 3638 4022 dc5: 3639 4023 cmd_mode = 2; 4024 undo_queue_commit(); 3640 4025 break; 3641 4026 case KEYCODE_DELETE: 3642 c = 'x'; 3643 // fall through 4027 if (dot < end - 1) 4028 dot = yank_delete(dot, dot, 1, YANKDEL, ALLOW_UNDO); 4029 break; 3644 4030 case 'X': // X- delete char before dot 3645 4031 case 'x': // x- delete the current char … … 3652 4038 if (c == 'X') 3653 4039 dot--; // delete prev char 3654 dot = yank_delete(dot, dot, 0, YANKDEL ); // delete char4040 dot = yank_delete(dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete char 3655 4041 } 3656 4042 } while (--cmdcnt > 0); … … 3663 4049 c1 = get_one_char(); 3664 4050 if (c1 != 'Z') { 3665 indicate_error( c);4051 indicate_error(); 3666 4052 break; 3667 4053 } 3668 if ( file_modified) {4054 if (modified_count) { 3669 4055 if (ENABLE_FEATURE_VI_READONLY && readonly_mode) { 3670 status_line_bold(" \"%s\" Fileis read only", current_filename);4056 status_line_bold("'%s' is read only", current_filename); 3671 4057 break; 3672 4058 } … … 3723 4109 // determine range, and whether it spans lines 3724 4110 ml = find_range(&p, &q, c1); 4111 place_cursor(0, 0); 3725 4112 if (c1 == 27) { // ESC- user changed mind and wants out 3726 4113 c = c1 = 27; // Escape- do nothing … … 3734 4121 } 3735 4122 } 3736 dot = yank_delete(p, q, ml, yf ); // delete word4123 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete word 3737 4124 } else if (strchr("^0bBeEft%$ lh\b\177", c1)) { 3738 4125 // partial line copy text into a register and delete 3739 dot = yank_delete(p, q, ml, yf ); // delete word4126 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete word 3740 4127 } else if (strchr("cdykjHL+-{}\r\n", c1)) { 3741 4128 // whole line copy text into a register and delete 3742 dot = yank_delete(p, q, ml, yf ); // delete lines4129 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete lines 3743 4130 whole = 1; 3744 4131 } else { … … 3746 4133 c = c1 = 27; // error- 3747 4134 ml = 0; 3748 indicate_error( c);4135 indicate_error(); 3749 4136 } 3750 4137 if (ml && whole) { 3751 4138 if (c == 'c') { 3752 dot = char_insert(dot, '\n' );4139 dot = char_insert(dot, '\n', ALLOW_UNDO_CHAIN); 3753 4140 // on the last line of file don't move to prev line 3754 4141 if (whole && dot != (end-1)) { … … 3796 4183 c1 = get_one_char(); // get the replacement char 3797 4184 if (*dot != '\n') { 4185 #if ENABLE_FEATURE_VI_UNDO 4186 undo_push(dot, 1, UNDO_DEL); 3798 4187 *dot = c1; 3799 file_modified++; 4188 undo_push(dot, 1, UNDO_INS_CHAIN); 4189 #else 4190 *dot = c1; 4191 modified_count++; 4192 #endif 3800 4193 } 3801 4194 end_cmd_q(); // stop adding to q … … 3837 4230 case '~': // ~- flip the case of letters a-z -> A-Z 3838 4231 do { 4232 #if ENABLE_FEATURE_VI_UNDO 4233 if (islower(*dot)) { 4234 undo_push(dot, 1, UNDO_DEL); 4235 *dot = toupper(*dot); 4236 undo_push(dot, 1, UNDO_INS_CHAIN); 4237 } else if (isupper(*dot)) { 4238 undo_push(dot, 1, UNDO_DEL); 4239 *dot = tolower(*dot); 4240 undo_push(dot, 1, UNDO_INS_CHAIN); 4241 } 4242 #else 3839 4243 if (islower(*dot)) { 3840 4244 *dot = toupper(*dot); 3841 file_modified++;4245 modified_count++; 3842 4246 } else if (isupper(*dot)) { 3843 4247 *dot = tolower(*dot); 3844 file_modified++; 3845 } 4248 modified_count++; 4249 } 4250 #endif 3846 4251 dot_right(); 3847 4252 } while (--cmdcnt > 0); … … 3873 4278 // if text[] just became empty, add back an empty line 3874 4279 if (end == text) { 3875 char_insert(text, '\n' ); // start empty buf with dummy line4280 char_insert(text, '\n', NO_UNDO); // start empty buf with dummy line 3876 4281 dot = text; 3877 4282 }
Note:
See TracChangeset
for help on using the changeset viewer.