Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/editors
- Timestamp:
- Feb 25, 2011, 9:26:54 PM (13 years ago)
- Location:
- branches/2.2.9/mindi-busybox/editors
- Files:
-
- 6 added
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2.2.9/mindi-busybox/editors/Config.in
r1765 r2725 1 # DO NOT EDIT. This file is generated from Config.src 1 2 # 2 3 # For a description of the syntax of this configuration file, … … 6 7 menu "Editors" 7 8 9 config PATCH 10 bool "patch" 11 default y 12 help 13 Apply a unified diff formatted patch. 14 8 15 config AWK 9 16 bool "awk" 10 default n11 help 12 Awk is used as a pattern scanning and processing language. 17 default y 18 help 19 Awk is used as a pattern scanning and processing language. This is 13 20 the BusyBox implementation of that programming language. 14 21 15 config FEATURE_AWK_ MATH22 config FEATURE_AWK_LIBM 16 23 bool "Enable math functions (requires libm)" 17 24 default y … … 23 30 config CMP 24 31 bool "cmp" 25 default n32 default y 26 33 help 27 34 cmp is used to compare two files and returns the result … … 30 37 config DIFF 31 38 bool "diff" 32 default n39 default y 33 40 help 34 41 diff compares two files or directories and outputs the … … 36 43 the patch command. 37 44 38 config FEATURE_DIFF_BINARY 39 bool "Enable checks for binary files" 40 default y 41 depends on DIFF 42 help 43 This option enables support for checking for binary files 44 before a comparison is carried out. 45 config FEATURE_DIFF_LONG_OPTIONS 46 bool "Enable long options" 47 default y 48 depends on DIFF && LONG_OPTS 49 help 50 Enable use of long options. 45 51 46 52 config FEATURE_DIFF_DIR … … 52 58 comparison. 53 59 54 config FEATURE_DIFF_MINIMAL55 bool "Enable -d option to find smaller sets of changes"56 default n57 depends on DIFF58 help59 Enabling this option allows the use of -d to make diff60 try hard to find the smallest possible set of changes.61 62 60 config ED 63 61 bool "ed" 64 default n62 default y 65 63 help 66 64 The original 1970's Unix text editor, from the days of teletypes. 67 Small, simple, evil. Part of SUSv3.If you're not already using65 Small, simple, evil. Part of SUSv3. If you're not already using 68 66 this, you don't need it. 69 70 config PATCH71 bool "patch"72 default n73 help74 Apply a unified diff formatted patch.75 67 76 68 config SED 77 69 bool "sed" 78 default n70 default y 79 71 help 80 72 sed is used to perform text transformations on a file … … 83 75 config VI 84 76 bool "vi" 77 default y 78 help 79 'vi' is a text editor. More specifically, it is the One True 80 text editor <grin>. It does, however, have a rather steep 81 learning curve. If you are not already comfortable with 'vi' 82 you may wish to use something else. 83 84 config FEATURE_VI_MAX_LEN 85 int "Maximum screen width in vi" 86 range 256 16384 87 default 4096 88 depends on VI 89 help 90 Contrary to what you may think, this is not eating much. 91 Make it smaller than 4k only if you are very limited on memory. 92 93 config FEATURE_VI_8BIT 94 bool "Allow vi to display 8-bit chars (otherwise shows dots)" 85 95 default n 86 help 87 'vi' is a text editor. More specifically, it is the One True 88 text editor <grin>. It does, however, have a rather steep 89 learning curve. If you are not already comfortable with 'vi' 90 you may wish to use something else. 91 92 config FEATURE_VI_MAX_LEN 93 int "Maximum line length in vi" 94 range 256 16384 95 default 1024 96 depends on VI 97 help 98 vi uses on-stack buffers for intermediate line buffers. 99 You may want to decrease this parameter if your target machine 100 benefits from smaller stack usage. 96 depends on VI 97 help 98 If your terminal can display characters with high bit set, 99 you may want to enable this. Note: vi is not Unicode-capable. 100 If your terminal combines several 8-bit bytes into one character 101 (as in Unicode mode), this will not work properly. 101 102 102 103 config FEATURE_VI_COLON … … 105 106 depends on VI 106 107 help 107 Enable a limited set of colon commands for vi. 108 Enable a limited set of colon commands for vi. This does not 108 109 provide an "ex" mode. 109 110 … … 129 130 depends on VI 130 131 help 131 Selecting this option will make busybox vi signal aware. 132 Selecting this option will make busybox vi signal aware. This will 132 133 make busybox vi support SIGWINCH to deal with Window Changes, catch 133 134 Ctrl-Z and Ctrl-C and alarms. … … 169 170 Make busybox vi behave nicely with terminals that get resized. 170 171 172 config FEATURE_VI_ASK_TERMINAL 173 bool "Use 'tell me cursor position' ESC sequence to measure window" 174 default y 175 depends on VI 176 help 177 If terminal size can't be retrieved and $LINES/$COLUMNS are not set, 178 this option makes vi perform a last-ditch effort to find it: 179 vi positions cursor to 999,999 and asks terminal to report real 180 cursor position using "ESC [ 6 n" escape sequence, then reads stdin. 181 182 This is not clean but helps a lot on serial lines and such. 183 171 184 config FEATURE_VI_OPTIMIZE_CURSOR 172 185 bool "Optimize cursor movement" -
branches/2.2.9/mindi-busybox/editors/Kbuild
r1765 r2725 1 # DO NOT EDIT. This file is generated from Kbuild.src 1 2 # Makefile for busybox 2 3 # 3 4 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org> 4 5 # 5 # Licensed under the GPL v2, see the file LICENSE in this tarball.6 # Licensed under GPLv2, see file LICENSE in this source tree. 6 7 7 8 lib-y:= 9 10 lib-$(CONFIG_PATCH) += patch.o 8 11 lib-$(CONFIG_AWK) += awk.o 9 12 lib-$(CONFIG_CMP) += cmp.o 10 13 lib-$(CONFIG_DIFF) += diff.o 11 14 lib-$(CONFIG_ED) += ed.o 12 lib-$(CONFIG_PATCH) += patch.o13 15 lib-$(CONFIG_SED) += sed.o 14 16 lib-$(CONFIG_VI) += vi.o -
branches/2.2.9/mindi-busybox/editors/awk.c
r1765 r2725 5 5 * Copyright (C) 2002 by Dmitry Zakharov <dmit@crp.bank.gov.ua> 6 6 * 7 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.7 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 8 8 */ 9 9 … … 11 11 #include "xregex.h" 12 12 #include <math.h> 13 extern char **environ;14 13 15 14 /* This is a NOEXEC applet. Be very careful! */ 15 16 17 /* If you comment out one of these below, it will be #defined later 18 * to perform debug printfs to stderr: */ 19 #define debug_printf_walker(...) do {} while (0) 20 #define debug_printf_eval(...) do {} while (0) 21 22 #ifndef debug_printf_walker 23 # define debug_printf_walker(...) (fprintf(stderr, __VA_ARGS__)) 24 #endif 25 #ifndef debug_printf_eval 26 # define debug_printf_eval(...) (fprintf(stderr, __VA_ARGS__)) 27 #endif 28 16 29 17 30 … … 33 46 /* these flags are static, don't change them when value is changed */ 34 47 #define VF_DONTTOUCH (VF_ARRAY | VF_SPECIAL | VF_WALK | VF_CHILD | VF_DIRTY) 48 49 typedef struct walker_list { 50 char *end; 51 char *cur; 52 struct walker_list *prev; 53 char wbuf[1]; 54 } walker_list; 35 55 36 56 /* Variable */ … … 43 63 struct xhash_s *array; /* array ptr */ 44 64 struct var_s *parent; /* for func args, ptr to actual parameter */ 45 char **walker;/* list of array elements (for..in) */65 walker_list *walker; /* list of array elements (for..in) */ 46 66 } x; 47 67 } var; … … 95 115 struct node_s *n; 96 116 var *v; 97 int i;98 char * s;117 int aidx; 118 char *new_progname; 99 119 regex_t *re; 100 120 } l; … … 103 123 regex_t *ire; 104 124 func *f; 105 int argno;106 125 } r; 107 126 union { … … 116 135 struct nvblock_s *prev; 117 136 struct nvblock_s *next; 118 var nv[ 0];137 var nv[]; 119 138 } nvblock; 120 139 … … 252 271 /* builtins */ 253 272 enum { 254 B_a2, B_ix, B_ma, B_sp, B_ss, B_ti, B_lo, B_up,273 B_a2, B_ix, B_ma, B_sp, B_ss, B_ti, B_mt, B_lo, B_up, 255 274 B_ge, B_gs, B_su, 256 275 B_an, B_co, B_ls, B_or, B_rs, B_xo, … … 259 278 /* tokens and their corresponding info values */ 260 279 261 #define 262 #define 263 264 #define OC_BOC_BUILTIN280 #define NTC "\377" /* switch to next token class (tc<<1) */ 281 #define NTCC '\377' 282 283 #define OC_B OC_BUILTIN 265 284 266 285 static const char tokenlist[] ALIGN1 = 267 "\1(" NTC 268 "\1)" NTC 269 "\1/" NTC /* REGEXP */ 270 "\2>>" "\1>" "\1|" NTC /* OUTRDR */ 271 "\2++" "\2--" NTC /* UOPPOST */ 272 "\2++" "\2--" "\1$" NTC /* UOPPRE1 */ 273 "\2==" "\1=" "\2+=" "\2-=" /* BINOPX */ 274 "\2*=" "\2/=" "\2%=" "\2^=" 275 "\1+" "\1-" "\3**=" "\2**" 276 "\1/" "\1%" "\1^" "\1*" 277 "\2!=" "\2>=" "\2<=" "\1>" 278 "\1<" "\2!~" "\1~" "\2&&" 279 "\2||" "\1?" "\1:" NTC 280 "\2in" NTC 281 "\1," NTC 282 "\1|" NTC 283 "\1+" "\1-" "\1!" NTC /* UOPPRE2 */ 284 "\1]" NTC 285 "\1{" NTC 286 "\1}" NTC 287 "\1;" NTC 288 "\1\n" NTC 289 "\2if" "\2do" "\3for" "\5break" /* STATX */ 290 "\10continue" "\6delete" "\5print" 291 "\6printf" "\4next" "\10nextfile" 292 "\6return" "\4exit" NTC 293 "\5while" NTC 294 "\4else" NTC 295 296 "\3and" "\5compl" "\6lshift" "\2or" 297 "\6rshift" "\3xor" 298 "\5close" "\6system" "\6fflush" "\5atan2" /* BUILTIN */ 299 "\3cos" "\3exp" "\3int" "\3log" 300 "\4rand" "\3sin" "\4sqrt" "\5srand" 301 "\6gensub" "\4gsub" "\5index" "\6length" 302 "\5match" "\5split" "\7sprintf" "\3sub" 303 "\6substr" "\7systime" "\10strftime" 304 "\7tolower" "\7toupper" NTC 305 "\7getline" NTC 306 "\4func" "\10function" NTC 307 "\5BEGIN" NTC 308 "\3END" "\0" 286 "\1(" NTC 287 "\1)" NTC 288 "\1/" NTC /* REGEXP */ 289 "\2>>" "\1>" "\1|" NTC /* OUTRDR */ 290 "\2++" "\2--" NTC /* UOPPOST */ 291 "\2++" "\2--" "\1$" NTC /* UOPPRE1 */ 292 "\2==" "\1=" "\2+=" "\2-=" /* BINOPX */ 293 "\2*=" "\2/=" "\2%=" "\2^=" 294 "\1+" "\1-" "\3**=" "\2**" 295 "\1/" "\1%" "\1^" "\1*" 296 "\2!=" "\2>=" "\2<=" "\1>" 297 "\1<" "\2!~" "\1~" "\2&&" 298 "\2||" "\1?" "\1:" NTC 299 "\2in" NTC 300 "\1," NTC 301 "\1|" NTC 302 "\1+" "\1-" "\1!" NTC /* UOPPRE2 */ 303 "\1]" NTC 304 "\1{" NTC 305 "\1}" NTC 306 "\1;" NTC 307 "\1\n" NTC 308 "\2if" "\2do" "\3for" "\5break" /* STATX */ 309 "\10continue" "\6delete" "\5print" 310 "\6printf" "\4next" "\10nextfile" 311 "\6return" "\4exit" NTC 312 "\5while" NTC 313 "\4else" NTC 314 315 "\3and" "\5compl" "\6lshift" "\2or" 316 "\6rshift" "\3xor" 317 "\5close" "\6system" "\6fflush" "\5atan2" /* BUILTIN */ 318 "\3cos" "\3exp" "\3int" "\3log" 319 "\4rand" "\3sin" "\4sqrt" "\5srand" 320 "\6gensub" "\4gsub" "\5index" "\6length" 321 "\5match" "\5split" "\7sprintf" "\3sub" 322 "\6substr" "\7systime" "\10strftime" "\6mktime" 323 "\7tolower" "\7toupper" NTC 324 "\7getline" NTC 325 "\4func" "\10function" NTC 326 "\5BEGIN" NTC 327 "\3END" 328 /* compiler adds trailing "\0" */ 309 329 ; 310 330 … … 313 333 0, 314 334 OC_REGEXP, 315 xS|'a', xS|'w', xS|'|', 316 OC_UNARY|xV|P(9)|'p', OC_UNARY|xV|P(9)|'m', 317 OC_UNARY|xV|P(9)|'P', OC_UNARY|xV|P(9)|'M', 318 OC_FIELD|xV|P(5), 319 OC_COMPARE|VV|P(39)|5, OC_MOVE|VV|P(74), 320 OC_REPLACE|NV|P(74)|'+', OC_REPLACE|NV|P(74)|'-', 321 OC_REPLACE|NV|P(74)|'*', OC_REPLACE|NV|P(74)|'/', 322 OC_REPLACE|NV|P(74)|'%', OC_REPLACE|NV|P(74)|'&', 323 OC_BINARY|NV|P(29)|'+', OC_BINARY|NV|P(29)|'-', 324 OC_REPLACE|NV|P(74)|'&', OC_BINARY|NV|P(15)|'&', 325 OC_BINARY|NV|P(25)|'/', OC_BINARY|NV|P(25)|'%', 326 OC_BINARY|NV|P(15)|'&', OC_BINARY|NV|P(25)|'*', 327 OC_COMPARE|VV|P(39)|4, OC_COMPARE|VV|P(39)|3, 328 OC_COMPARE|VV|P(39)|0, OC_COMPARE|VV|P(39)|1, 329 OC_COMPARE|VV|P(39)|2, OC_MATCH|Sx|P(45)|'!', 330 OC_MATCH|Sx|P(45)|'~', OC_LAND|Vx|P(55), 331 OC_LOR|Vx|P(59), OC_TERNARY|Vx|P(64)|'?', 332 OC_COLON|xx|P(67)|':', 333 OC_IN|SV|P(49), 335 xS|'a', xS|'w', xS|'|', 336 OC_UNARY|xV|P(9)|'p', OC_UNARY|xV|P(9)|'m', 337 OC_UNARY|xV|P(9)|'P', OC_UNARY|xV|P(9)|'M', OC_FIELD|xV|P(5), 338 OC_COMPARE|VV|P(39)|5, OC_MOVE|VV|P(74), OC_REPLACE|NV|P(74)|'+', OC_REPLACE|NV|P(74)|'-', 339 OC_REPLACE|NV|P(74)|'*', OC_REPLACE|NV|P(74)|'/', OC_REPLACE|NV|P(74)|'%', OC_REPLACE|NV|P(74)|'&', 340 OC_BINARY|NV|P(29)|'+', OC_BINARY|NV|P(29)|'-', OC_REPLACE|NV|P(74)|'&', OC_BINARY|NV|P(15)|'&', 341 OC_BINARY|NV|P(25)|'/', OC_BINARY|NV|P(25)|'%', OC_BINARY|NV|P(15)|'&', OC_BINARY|NV|P(25)|'*', 342 OC_COMPARE|VV|P(39)|4, OC_COMPARE|VV|P(39)|3, OC_COMPARE|VV|P(39)|0, OC_COMPARE|VV|P(39)|1, 343 OC_COMPARE|VV|P(39)|2, OC_MATCH|Sx|P(45)|'!', OC_MATCH|Sx|P(45)|'~', OC_LAND|Vx|P(55), 344 OC_LOR|Vx|P(59), OC_TERNARY|Vx|P(64)|'?', OC_COLON|xx|P(67)|':', 345 OC_IN|SV|P(49), /* in */ 334 346 OC_COMMA|SS|P(80), 335 347 OC_PGETLINE|SV|P(37), 336 OC_UNARY|xV|P(19)|'+', OC_UNARY|xV|P(19)|'-',337 OC_UNARY|xV|P(19)|'!',348 OC_UNARY|xV|P(19)|'+', OC_UNARY|xV|P(19)|'-', OC_UNARY|xV|P(19)|'!', 349 0, /* ] */ 338 350 0, 339 351 0, 340 352 0, 341 0, 342 0, 343 ST_IF, ST_DO, ST_FOR, OC_BREAK, 344 OC_CONTINUE, OC_DELETE|Vx, OC_PRINT, 345 OC_PRINTF, OC_NEXT, OC_NEXTFILE, 346 OC_RETURN|Vx, OC_EXIT|Nx, 353 0, /* \n */ 354 ST_IF, ST_DO, ST_FOR, OC_BREAK, 355 OC_CONTINUE, OC_DELETE|Vx, OC_PRINT, 356 OC_PRINTF, OC_NEXT, OC_NEXTFILE, 357 OC_RETURN|Vx, OC_EXIT|Nx, 347 358 ST_WHILE, 348 0, 359 0, /* else */ 349 360 350 361 OC_B|B_an|P(0x83), OC_B|B_co|P(0x41), OC_B|B_ls|P(0x83), OC_B|B_or|P(0x83), … … 355 366 OC_B|B_ge|P(0xd6), OC_B|B_gs|P(0xb6), OC_B|B_ix|P(0x9b), OC_FBLTIN|Sx|F_le, 356 367 OC_B|B_ma|P(0x89), OC_B|B_sp|P(0x8b), OC_SPRINTF, OC_B|B_su|P(0xb6), 357 OC_B|B_ss|P(0x8f), OC_FBLTIN|F_ti, OC_B|B_ti|P(0x0b), 368 OC_B|B_ss|P(0x8f), OC_FBLTIN|F_ti, OC_B|B_ti|P(0x0b), OC_B|B_mt|P(0x0b), 358 369 OC_B|B_lo|P(0x49), OC_B|B_up|P(0x49), 359 370 OC_GETLINE|SV|P(0), 360 0, 371 0, 0, 361 372 0, 362 0 373 0 /* END */ 363 374 }; 364 375 … … 368 379 CONVFMT, OFMT, FS, OFS, 369 380 ORS, RS, RT, FILENAME, 370 SUBSEP, ARGIND, ARGC, ARGV, 371 ERRNO, FNR, 372 NR, NF, IGNORECASE, 373 ENVIRON, F0, NUM_INTERNAL_VARS 381 SUBSEP, F0, ARGIND, ARGC, 382 ARGV, ERRNO, FNR, NR, 383 NF, IGNORECASE, ENVIRON, NUM_INTERNAL_VARS 374 384 }; 375 385 … … 377 387 "CONVFMT\0" "OFMT\0" "FS\0*" "OFS\0" 378 388 "ORS\0" "RS\0*" "RT\0" "FILENAME\0" 379 "SUBSEP\0" "ARGIND\0" "ARGC\0" "ARGV\0" 380 "ERRNO\0" "FNR\0" 381 "NR\0" "NF\0*" "IGNORECASE\0*" 382 "ENVIRON\0" "$\0*" "\0"; 389 "SUBSEP\0" "$\0*" "ARGIND\0" "ARGC\0" 390 "ARGV\0" "ERRNO\0" "FNR\0" "NR\0" 391 "NF\0*" "IGNORECASE\0*" "ENVIRON\0" "\0"; 383 392 384 393 static const char vValues[] ALIGN1 = 385 394 "%.6g\0" "%.6g\0" " \0" " \0" 386 395 "\n\0" "\n\0" "\0" "\0" 387 "\034\0" 388 "\377"; 396 "\034\0" "\0" "\377"; 389 397 390 398 /* hash size may grow to these values */ … … 394 402 395 403 /* Globals. Split in two parts so that first one is addressed 396 * with (mostly short) negative offsets */ 404 * with (mostly short) negative offsets. 405 * NB: it's unsafe to put members of type "double" 406 * into globals2 (gcc may fail to align them). 407 */ 397 408 struct globals { 398 chain beginseq, mainseq, endseq, *seq; 409 double t_double; 410 chain beginseq, mainseq, endseq; 411 chain *seq; 399 412 node *break_ptr, *continue_ptr; 400 413 rstream *iF; … … 443 456 444 457 /* biggest and least used members go last */ 445 double t_double;446 458 tsplitter fsplitter, rsplitter; 447 459 }; 448 460 #define G1 (ptr_to_globals[-1]) 449 #define G (*(struct globals2 * const)ptr_to_globals)461 #define G (*(struct globals2 *)ptr_to_globals) 450 462 /* For debug. nm --size-sort awk.o | grep -vi ' [tr] ' */ 451 /* char G1size[sizeof(G1)]; - 0x6c*/452 /* char Gsize[sizeof(G)]; - 0x1cc*/463 /*char G1size[sizeof(G1)]; - 0x74 */ 464 /*char Gsize[sizeof(G)]; - 0x1c4 */ 453 465 /* Trying to keep most of members accessible with short offsets: */ 454 /* char Gofs_seed[offsetof(struct globals2, evaluate__seed)]; - 0x90 */ 466 /*char Gofs_seed[offsetof(struct globals2, evaluate__seed)]; - 0x90 */ 467 #define t_double (G1.t_double ) 455 468 #define beginseq (G1.beginseq ) 456 469 #define mainseq (G1.mainseq ) … … 480 493 #define t_tclass (G.t_tclass ) 481 494 #define t_string (G.t_string ) 482 #define t_double (G.t_double )483 495 #define t_lineno (G.t_lineno ) 484 496 #define t_rollback (G.t_rollback ) … … 487 499 #define rsplitter (G.rsplitter ) 488 500 #define INIT_G() do { \ 489 PTR_TO_GLOBALS = xzalloc(sizeof(G1) + sizeof(G)) + sizeof(G1); \501 SET_PTR_TO_GLOBALS((char*)xzalloc(sizeof(G1)+sizeof(G)) + sizeof(G1)); \ 490 502 G.next_token__ltclass = TC_OPTERM; \ 491 503 G.evaluate__seed = 1; \ … … 500 512 static rstream *next_input_file(void); 501 513 static int fmt_num(char *, int, const char *, double, int); 502 static int awk_exit(int) ATTRIBUTE_NORETURN;514 static int awk_exit(int) NORETURN; 503 515 504 516 /* ---- error handling ---- */ … … 513 525 static const char EMSG_POSSIBLE_ERROR[] ALIGN1 = "Possible syntax error"; 514 526 static const char EMSG_UNDEF_FUNC[] ALIGN1 = "Call to undefined function"; 515 #if !ENABLE_FEATURE_AWK_MATH516 527 static const char EMSG_NO_MATH[] ALIGN1 = "Math support is not compiled in"; 517 #endif 518 519 static void zero_out_var(var * vp) 528 529 static void zero_out_var(var *vp) 520 530 { 521 531 memset(vp, 0, sizeof(*vp)); 522 532 } 523 533 524 static void syntax_error(const char * const message) ATTRIBUTE_NORETURN;525 static void syntax_error(const char * constmessage)534 static void syntax_error(const char *message) NORETURN; 535 static void syntax_error(const char *message) 526 536 { 527 537 bb_error_msg_and_die("%s:%i: %s", g_progname, g_lineno, message); … … 534 544 unsigned idx = 0; 535 545 536 while (*name) idx = *name++ + (idx << 6) - idx; 546 while (*name) 547 idx = *name++ + (idx << 6) - idx; 537 548 return idx; 538 549 } … … 543 554 xhash *newhash; 544 555 545 newhash = xzalloc(sizeof( xhash));556 newhash = xzalloc(sizeof(*newhash)); 546 557 newhash->csize = FIRST_PRIME; 547 newhash->items = xzalloc( newhash->csize * sizeof(hash_item *));558 newhash->items = xzalloc(FIRST_PRIME * sizeof(newhash->items[0])); 548 559 549 560 return newhash; … … 555 566 hash_item *hi; 556 567 557 hi = hash->items [ hashidx(name) % hash->csize];568 hi = hash->items[hashidx(name) % hash->csize]; 558 569 while (hi) { 559 570 if (strcmp(hi->name, name) == 0) 560 return & (hi->data);571 return &hi->data; 561 572 hi = hi->next; 562 573 } … … 574 585 575 586 newsize = PRIMES[hash->nprime++]; 576 newitems = xzalloc(newsize * sizeof( hash_item *));587 newitems = xzalloc(newsize * sizeof(newitems[0])); 577 588 578 589 for (i = 0; i < hash->csize; i++) { … … 605 616 606 617 l = strlen(name) + 1; 607 hi = xzalloc(sizeof( hash_item) + l);608 memcpy(hi->name, name, l);618 hi = xzalloc(sizeof(*hi) + l); 619 strcpy(hi->name, name); 609 620 610 621 idx = hashidx(name) % hash->csize; … … 613 624 hash->glen += l; 614 625 } 615 return & (hi->data);626 return &hi->data; 616 627 } 617 628 … … 625 636 hash_item *hi, **phi; 626 637 627 phi = & (hash->items[hashidx(name) % hash->csize]);638 phi = &hash->items[hashidx(name) % hash->csize]; 628 639 while (*phi) { 629 640 hi = *phi; … … 635 646 break; 636 647 } 637 phi = & (hi->next);648 phi = &hi->next; 638 649 } 639 650 } … … 641 652 /* ------ some useful functions ------ */ 642 653 643 static void skip_spaces(char **s) 644 { 645 char *p = *s; 646 654 static char *skip_spaces(char *p) 655 { 647 656 while (1) { 648 657 if (*p == '\\' && p[1] == '\n') { … … 654 663 p++; 655 664 } 656 *s = p; 657 } 658 665 return p; 666 } 667 668 /* returns old *s, advances *s past word and terminating NUL */ 659 669 static char *nextword(char **s) 660 670 { 661 671 char *p = *s; 662 663 while (*(*s)++) /* */; 664 672 while (*(*s)++ != '\0') 673 continue; 665 674 return p; 666 675 } … … 670 679 char c, *pps; 671 680 672 c = *( (*s)++);681 c = *(*s)++; 673 682 pps = *s; 674 if (c == '\\') c = bb_process_escape_sequence((const char**)s); 675 if (c == '\\' && *s == pps) c = *((*s)++); 683 if (c == '\\') 684 c = bb_process_escape_sequence((const char**)s); 685 if (c == '\\' && *s == pps) { /* unrecognized \z? */ 686 c = *(*s); /* yes, fetch z */ 687 if (c) 688 (*s)++; /* advance unless z = NUL */ 689 } 676 690 return c; 677 691 } 678 692 679 static int ALWAYS_INLINEisalnum_(int c)693 static ALWAYS_INLINE int isalnum_(int c) 680 694 { 681 695 return (isalnum(c) || c == '_'); 682 696 } 683 697 684 static FILE *afopen(const char *path, const char *mode) 685 { 686 return (*path == '-' && *(path+1) == '\0') ? stdin : xfopen(path, mode); 698 static double my_strtod(char **pp) 699 { 700 char *cp = *pp; 701 if (ENABLE_DESKTOP && cp[0] == '0') { 702 /* Might be hex or octal integer: 0x123abc or 07777 */ 703 char c = (cp[1] | 0x20); 704 if (c == 'x' || isdigit(cp[1])) { 705 unsigned long long ull = strtoull(cp, pp, 0); 706 if (c == 'x') 707 return ull; 708 c = **pp; 709 if (!isdigit(c) && c != '.') 710 return ull; 711 /* else: it may be a floating number. Examples: 712 * 009.123 (*pp points to '9') 713 * 000.123 (*pp points to '.') 714 * fall through to strtod. 715 */ 716 } 717 } 718 return strtod(cp, pp); 687 719 } 688 720 … … 748 780 } 749 781 750 /* same as setvar_s but set USER flag */782 /* same as setvar_s but sets USER flag */ 751 783 static var *setvar_u(var *v, const char *value) 752 784 { 753 setvar_s(v, value);785 v = setvar_s(v, value); 754 786 v->type |= VF_USER; 755 787 return v; … … 759 791 static void setari_u(var *a, int idx, const char *s) 760 792 { 761 char sidx[sizeof(int)*3 + 1];762 793 var *v; 763 794 764 sprintf(sidx, "%d", idx); 765 v = findvar(iamarray(a), sidx); 795 v = findvar(iamarray(a), itoa(idx)); 766 796 setvar_u(v, s); 767 797 } … … 796 826 s = v->string; 797 827 if (s && *s) { 798 v->number = strtod(s, &s); 828 debug_printf_eval("getvar_i: '%s'->", s); 829 v->number = my_strtod(&s); 830 debug_printf_eval("%f (s:'%s')\n", v->number, s); 799 831 if (v->type & VF_USER) { 800 s kip_spaces(&s);832 s = skip_spaces(s); 801 833 if (*s != '\0') 802 834 v->type &= ~VF_USER; 803 835 } 804 836 } else { 837 debug_printf_eval("getvar_i: '%s'->zero\n", s); 805 838 v->type &= ~VF_USER; 806 839 } 807 840 v->type |= VF_CACHED; 808 841 } 842 debug_printf_eval("getvar_i: %f\n", v->number); 809 843 return v->number; 844 } 845 846 /* Used for operands of bitwise ops */ 847 static unsigned long getvar_i_int(var *v) 848 { 849 double d = getvar_i(v); 850 851 /* Casting doubles to longs is undefined for values outside 852 * of target type range. Try to widen it as much as possible */ 853 if (d >= 0) 854 return (unsigned long)d; 855 /* Why? Think about d == -4294967295.0 (assuming 32bit longs) */ 856 return - (long) (unsigned long) (-d); 810 857 } 811 858 … … 815 862 clrvar(dest); 816 863 dest->type |= (src->type & ~(VF_DONTTOUCH | VF_FSTR)); 864 debug_printf_eval("copyvar: number:%f string:'%s'\n", src->number, src->string); 817 865 dest->number = src->number; 818 866 if (src->string) … … 825 873 static var *incvar(var *v) 826 874 { 827 return setvar_i(v, getvar_i(v) + 1. );875 return setvar_i(v, getvar_i(v) + 1.0); 828 876 } 829 877 … … 839 887 { 840 888 if (is_numeric(v)) 841 return (v->number == 0) ? 0 : 1;842 return (v->string && *(v->string)) ? 1 : 0;889 return (v->number != 0); 890 return (v->string && v->string[0]); 843 891 } 844 892 … … 852 900 while (g_cb) { 853 901 pb = g_cb; 854 if ((g_cb->pos - g_cb->nv) + n <= g_cb->size) break; 902 if ((g_cb->pos - g_cb->nv) + n <= g_cb->size) 903 break; 855 904 g_cb = g_cb->next; 856 905 } … … 858 907 if (!g_cb) { 859 908 size = (n <= MINNVBLOCK) ? MINNVBLOCK : n; 860 g_cb = x malloc(sizeof(nvblock) + size * sizeof(var));909 g_cb = xzalloc(sizeof(nvblock) + size * sizeof(var)); 861 910 g_cb->size = size; 862 911 g_cb->pos = g_cb->nv; 863 912 g_cb->prev = pb; 864 g_cb->next = NULL; 865 if (pb) pb->next = g_cb; 913 /*g_cb->next = NULL; - xzalloc did it */ 914 if (pb) 915 pb->next = g_cb; 866 916 } 867 917 … … 891 941 free(p->x.array); 892 942 } 893 if (p->type & VF_WALK) 894 free(p->x.walker); 895 943 if (p->type & VF_WALK) { 944 walker_list *n; 945 walker_list *w = p->x.walker; 946 debug_printf_walker("nvfree: freeing walker @%p\n", &p->x.walker); 947 p->x.walker = NULL; 948 while (w) { 949 n = w->prev; 950 debug_printf_walker(" free(%p)\n", w); 951 free(w); 952 w = n; 953 } 954 } 896 955 clrvar(p); 897 956 } … … 916 975 #define ltclass (G.next_token__ltclass) 917 976 918 char *p, * pp, *s;977 char *p, *s; 919 978 const char *tl; 920 979 uint32_t tc; 921 980 const uint32_t *ti; 922 int l;923 981 924 982 if (t_rollback) { … … 933 991 p = g_pos; 934 992 readnext: 935 skip_spaces(&p);993 p = skip_spaces(p); 936 994 g_lineno = t_lineno; 937 995 if (*p == '#') … … 949 1007 t_string = s = ++p; 950 1008 while (*p != '\"') { 1009 char *pp; 951 1010 if (*p == '\0' || *p == '\n') 952 1011 syntax_error(EMSG_UNEXP_EOS); 953 *(s++) = nextchar(&p); 1012 pp = p; 1013 *s++ = nextchar(&pp); 1014 p = pp; 954 1015 } 955 1016 p++; … … 965 1026 *s = *p++; 966 1027 if (*s++ == '\\') { 967 pp = p;968 *(s-1) = bb_process_escape_sequence((const char **)&p);969 if (*p p== '\\')1028 char *pp = p; 1029 s[-1] = bb_process_escape_sequence((const char **)&pp); 1030 if (*p == '\\') 970 1031 *s++ = '\\'; 971 if (p == pp)1032 if (pp == p) 972 1033 *s++ = *p++; 1034 else 1035 p = pp; 973 1036 } 974 1037 } … … 979 1042 } else if (*p == '.' || isdigit(*p)) { 980 1043 /* it's a number */ 981 t_double = strtod(p, &p); 1044 char *pp = p; 1045 t_double = my_strtod(&pp); 1046 p = pp; 982 1047 if (*p == '.') 983 1048 syntax_error(EMSG_UNEXP_TOKEN); … … 990 1055 ti = tokeninfo; 991 1056 while (*tl) { 992 l = *(tl++);993 if (l == NTCC) {1057 int l = (unsigned char) *tl++; 1058 if (l == (unsigned char) NTCC) { 994 1059 tc <<= 1; 995 1060 continue; 996 1061 } 997 /* if token class is expected, token998 * matches and it's not a longer word,999 * then this is what we are looking for1062 /* if token class is expected, 1063 * token matches, 1064 * and it's not a longer word, 1000 1065 */ 1001 1066 if ((tc & (expected | TC_WORD | TC_NEWLINE)) 1002 && *tl == *p &&strncmp(p, tl, l) == 01067 && strncmp(p, tl, l) == 0 1003 1068 && !((tc & TC_WORD) && isalnum_(p[l])) 1004 1069 ) { 1070 /* then this is what we are looking for */ 1005 1071 t_info = *ti; 1006 1072 p += l; 1007 break;1073 goto token_found; 1008 1074 } 1009 1075 ti++; 1010 1076 tl += l; 1011 1077 } 1012 1013 if (!*tl) { 1014 /* it's a name (var/array/function), 1015 * otherwise it's something wrong 1016 */ 1017 if (!isalnum_(*p)) 1018 syntax_error(EMSG_UNEXP_TOKEN); 1019 1020 t_string = --p; 1021 while (isalnum_(*(++p))) { 1022 *(p-1) = *p; 1078 /* not a known token */ 1079 1080 /* is it a name? (var/array/function) */ 1081 if (!isalnum_(*p)) 1082 syntax_error(EMSG_UNEXP_TOKEN); /* no */ 1083 /* yes */ 1084 t_string = --p; 1085 while (isalnum_(*++p)) { 1086 p[-1] = *p; 1087 } 1088 p[-1] = '\0'; 1089 tc = TC_VARIABLE; 1090 /* also consume whitespace between functionname and bracket */ 1091 if (!(expected & TC_VARIABLE) || (expected & TC_ARRAY)) 1092 p = skip_spaces(p); 1093 if (*p == '(') { 1094 tc = TC_FUNCTION; 1095 } else { 1096 if (*p == '[') { 1097 p++; 1098 tc = TC_ARRAY; 1023 1099 } 1024 *(p-1) = '\0'; 1025 tc = TC_VARIABLE; 1026 /* also consume whitespace between functionname and bracket */ 1027 if (!(expected & TC_VARIABLE)) 1028 skip_spaces(&p); 1029 if (*p == '(') { 1030 tc = TC_FUNCTION; 1031 } else { 1032 if (*p == '[') { 1033 p++; 1034 tc = TC_ARRAY; 1035 } 1036 } 1037 } 1100 } 1101 token_found: ; 1038 1102 } 1039 1103 g_pos = p; … … 1083 1147 } 1084 1148 1085 static node *mk_re_node(const char *s, node *n, regex_t *re)1149 static void mk_re_node(const char *s, node *n, regex_t *re) 1086 1150 { 1087 1151 n->info = OC_REGEXP; … … 1090 1154 xregcomp(re, s, REG_EXTENDED); 1091 1155 xregcomp(re + 1, s, REG_EXTENDED | REG_ICASE); 1092 1093 return n;1094 1156 } 1095 1157 … … 1115 1177 1116 1178 while (!((tc = next_token(xtc)) & iexp)) { 1179 1117 1180 if (glptr && (t_info == (OC_COMPARE | VV | P(39) | 2))) { 1118 1181 /* input redirection (<) attached to glptr node */ … … 1126 1189 * previous operators with higher priority */ 1127 1190 vn = cn; 1128 while ( ((t_info & PRIMASK) > (vn->a.n->info & PRIMASK2)) 1129 || ((t_info == vn->info) && ((t_info & OPCLSMASK) == OC_COLON)) ) 1191 while (((t_info & PRIMASK) > (vn->a.n->info & PRIMASK2)) 1192 || ((t_info == vn->info) && ((t_info & OPCLSMASK) == OC_COLON)) 1193 ) { 1130 1194 vn = vn->a.n; 1195 } 1131 1196 if ((t_info & OPCLSMASK) == OC_TERNARY) 1132 1197 t_info += P(6); … … 1167 1232 if (v != NULL) { 1168 1233 cn->info = OC_FNARG; 1169 cn->l. i= v->x.aidx;1234 cn->l.aidx = v->x.aidx; 1170 1235 } else { 1171 1236 cn->l.v = newvar(t_string); … … 1228 1293 seq->programname = g_progname; 1229 1294 n = chain_node(OC_NEWSOURCE); 1230 n->l. s= xstrdup(g_progname);1295 n->l.new_progname = xstrdup(g_progname); 1231 1296 } 1232 1297 … … 1283 1348 if (c & TC_GRPSTART) { 1284 1349 while (next_token(TC_GRPSEQ | TC_GRPTERM) != TC_GRPTERM) { 1285 if (t_tclass & TC_NEWLINE) continue; 1350 if (t_tclass & TC_NEWLINE) 1351 continue; 1286 1352 rollback_token(); 1287 1353 chain_group(); … … 1405 1471 while (next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) { 1406 1472 v = findvar(ahash, t_string); 1407 v->x.aidx = (f->nargs)++;1473 v->x.aidx = f->nargs++; 1408 1474 1409 1475 if (next_token(TC_COMMA | TC_SEQTERM) & TC_SEQTERM) 1410 1476 break; 1411 1477 } 1412 seq = & (f->body);1478 seq = &f->body; 1413 1479 chain_group(); 1414 1480 clear_array(ahash); … … 1448 1514 regfree(ire); // TODO: nuke ire, use re+1? 1449 1515 } 1450 if (s trlen(s) > 1) {1516 if (s[0] && s[1]) { /* strlen(s) > 1 */ 1451 1517 mk_re_node(s, n, re); 1452 1518 } else { 1453 n->info = (uint32_t) *s;1519 n->info = (uint32_t) s[0]; 1454 1520 } 1455 1521 … … 1463 1529 static regex_t *as_regex(node *op, regex_t *preg) 1464 1530 { 1531 int cflags; 1465 1532 var *v; 1466 1533 const char *s; … … 1471 1538 v = nvalloc(1); 1472 1539 s = getvar_s(evaluate(op, v)); 1473 xregcomp(preg, s, icase ? REG_EXTENDED | REG_ICASE : REG_EXTENDED); 1540 1541 cflags = icase ? REG_EXTENDED | REG_ICASE : REG_EXTENDED; 1542 /* Testcase where REG_EXTENDED fails (unpaired '{'): 1543 * echo Hi | awk 'gsub("@(samp|code|file)\{","");' 1544 * gawk 3.1.5 eats this. We revert to ~REG_EXTENDED 1545 * (maybe gsub is not supposed to use REG_EXTENDED?). 1546 */ 1547 if (regcomp(preg, s, cflags)) { 1548 cflags &= ~REG_EXTENDED; 1549 xregcomp(preg, s, cflags); 1550 } 1474 1551 nvfree(v); 1475 1552 return preg; 1476 1553 } 1477 1554 1478 /* gradually increasing buffer */ 1479 static void qrealloc(char **b, int n, int *size) 1480 { 1481 if (!*b || n >= *size) 1482 *b = xrealloc(*b, *size = n + (n>>1) + 80); 1555 /* gradually increasing buffer. 1556 * note that we reallocate even if n == old_size, 1557 * and thus there is at least one extra allocated byte. 1558 */ 1559 static char* qrealloc(char *b, int n, int *size) 1560 { 1561 if (!b || n >= *size) { 1562 *size = n + (n>>1) + 80; 1563 b = xrealloc(b, *size); 1564 } 1565 return b; 1483 1566 } 1484 1567 … … 1491 1574 i = maxfields; 1492 1575 maxfields = size + 16; 1493 Fields = xrealloc(Fields, maxfields * sizeof( var));1576 Fields = xrealloc(Fields, maxfields * sizeof(Fields[0])); 1494 1577 for (; i < maxfields; i++) { 1495 1578 Fields[i].type = VF_SPECIAL; … … 1497 1580 } 1498 1581 } 1499 1500 if (size < nfields) { 1501 for (i = size; i < nfields; i++) { 1502 clrvar(Fields + i); 1503 } 1582 /* if size < nfields, clear extra field variables */ 1583 for (i = size; i < nfields; i++) { 1584 clrvar(Fields + i); 1504 1585 } 1505 1586 nfields = size; … … 1508 1589 static int awk_split(const char *s, node *spl, char **slist) 1509 1590 { 1510 int l, n = 0;1591 int l, n; 1511 1592 char c[4]; 1512 1593 char *s1; … … 1522 1603 c[2] = '\n'; 1523 1604 1605 n = 0; 1524 1606 if ((spl->info & OPCLSMASK) == OC_REGEXP) { /* regex split */ 1525 1607 if (!*s) … … 1539 1621 } else { 1540 1622 pmatch[0].rm_eo = l; 1541 if (s[l]) pmatch[0].rm_eo++; 1623 if (s[l]) 1624 pmatch[0].rm_eo++; 1542 1625 } 1543 1626 memcpy(s1, s, l); 1544 s1[l] = '\0'; 1627 /* make sure we remove *all* of the separator chars */ 1628 do { 1629 s1[l] = '\0'; 1630 } while (++l < pmatch[0].rm_eo); 1545 1631 nextword(&s1); 1546 1632 s += pmatch[0].rm_eo; … … 1561 1647 c[1] = tolower(c[1]); 1562 1648 } 1563 if (*s1) n++; 1564 while ((s1 = strpbrk(s1, c))) { 1649 if (*s1) 1650 n++; 1651 while ((s1 = strpbrk(s1, c)) != NULL) { 1565 1652 *s1++ = '\0'; 1566 1653 n++; … … 1571 1658 while (*s) { 1572 1659 s = skip_whitespace(s); 1573 if (!*s) break; 1660 if (!*s) 1661 break; 1574 1662 n++; 1575 1663 while (*s && !isspace(*s)) … … 1636 1724 len += sl; 1637 1725 } 1638 qrealloc(&b, len+l+sl, &bsize);1726 b = qrealloc(b, len+l+sl, &bsize); 1639 1727 memcpy(b+len, s, l); 1640 1728 len += l; … … 1681 1769 static void hashwalk_init(var *v, xhash *array) 1682 1770 { 1683 char **w;1684 1771 hash_item *hi; 1685 int i; 1686 1687 if (v->type & VF_WALK) 1688 free(v->x.walker); 1689 1690 v->type |= VF_WALK; 1691 w = v->x.walker = xzalloc(2 + 2*sizeof(char *) + array->glen); 1692 w[0] = w[1] = (char *)(w + 2); 1772 unsigned i; 1773 walker_list *w; 1774 walker_list *prev_walker; 1775 1776 if (v->type & VF_WALK) { 1777 prev_walker = v->x.walker; 1778 } else { 1779 v->type |= VF_WALK; 1780 prev_walker = NULL; 1781 } 1782 debug_printf_walker("hashwalk_init: prev_walker:%p\n", prev_walker); 1783 1784 w = v->x.walker = xzalloc(sizeof(*w) + array->glen + 1); /* why + 1? */ 1785 debug_printf_walker(" walker@%p=%p\n", &v->x.walker, w); 1786 w->cur = w->end = w->wbuf; 1787 w->prev = prev_walker; 1693 1788 for (i = 0; i < array->csize; i++) { 1694 1789 hi = array->items[i]; 1695 1790 while (hi) { 1696 strcpy( *w, hi->name);1697 nextword( w);1791 strcpy(w->end, hi->name); 1792 nextword(&w->end); 1698 1793 hi = hi->next; 1699 1794 } … … 1703 1798 static int hashwalk_next(var *v) 1704 1799 { 1705 char **w; 1706 1707 w = v->x.walker; 1708 if (w[1] == w[0]) 1800 walker_list *w = v->x.walker; 1801 1802 if (w->cur >= w->end) { 1803 walker_list *prev_walker = w->prev; 1804 1805 debug_printf_walker("end of iteration, free(walker@%p:%p), prev_walker:%p\n", &v->x.walker, w, prev_walker); 1806 free(w); 1807 v->x.walker = prev_walker; 1709 1808 return FALSE; 1710 1711 setvar_s(v, nextword(w+1)); 1809 } 1810 1811 setvar_s(v, nextword(&w->cur)); 1712 1812 return TRUE; 1713 1813 } … … 1725 1825 char *b; 1726 1826 regmatch_t pmatch[2]; 1727 int a, p, pp=0, size;1827 int size, a, p, pp = 0; 1728 1828 int fd, so, eo, r, rp; 1729 1829 char c, *m, *s; 1830 1831 debug_printf_eval("entered %s()\n", __func__); 1730 1832 1731 1833 /* we're using our own buffer since we need access to accumulating … … 1740 1842 rp = 0; 1741 1843 1742 if (!m) qrealloc(&m, 256, &size); 1844 if (!m) 1845 m = qrealloc(m, 256, &size); 1846 1743 1847 do { 1744 1848 b = m + a; … … 1756 1860 } else if (c != '\0') { 1757 1861 s = strchr(b+pp, c); 1758 if (!s) s = memchr(b+pp, '\0', p - pp); 1862 if (!s) 1863 s = memchr(b+pp, '\0', p - pp); 1759 1864 if (s) { 1760 1865 so = eo = s-b; … … 1768 1873 if (s) { 1769 1874 so = eo = s-b; 1770 while (b[eo] == '\n') eo++; 1875 while (b[eo] == '\n') 1876 eo++; 1771 1877 if (b[eo] != '\0') 1772 1878 break; … … 1776 1882 1777 1883 if (a > 0) { 1778 memmove(m, (const void *)(m+a), p+1);1884 memmove(m, m+a, p+1); 1779 1885 b = m; 1780 1886 a = 0; 1781 1887 } 1782 1888 1783 qrealloc(&m, a+p+128, &size);1889 m = qrealloc(m, a+p+128, &size); 1784 1890 b = m + a; 1785 1891 pp = p; … … 1811 1917 rsm->size = size; 1812 1918 1919 debug_printf_eval("returning from %s(): %d\n", __func__, r); 1920 1813 1921 return r; 1814 1922 } … … 1835 1943 } 1836 1944 1837 1838 1945 /* formatted output into an allocated buffer, return ptr to buffer */ 1839 1946 static char *awk_printf(node *n) … … 1852 1959 while (*f) { 1853 1960 s = f; 1854 while (*f && (*f != '%' || * (++f)== '%'))1961 while (*f && (*f != '%' || *++f == '%')) 1855 1962 f++; 1856 1963 while (*f && !isalpha(*f)) { … … 1861 1968 1862 1969 incr = (f - s) + MAXVARFMT; 1863 qrealloc(&b, incr + i, &bsize);1970 b = qrealloc(b, incr + i, &bsize); 1864 1971 c = *f; 1865 if (c != '\0') f++; 1972 if (c != '\0') 1973 f++; 1866 1974 c1 = *f; 1867 1975 *f = '\0'; … … 1874 1982 } else if (c == 's') { 1875 1983 s1 = getvar_s(arg); 1876 qrealloc(&b, incr+i+strlen(s1), &bsize);1984 b = qrealloc(b, incr+i+strlen(s1), &bsize); 1877 1985 i += sprintf(b+i, s, s1); 1878 1986 } else { … … 1882 1990 1883 1991 /* if there was an error while sprintf, return value is negative */ 1884 if (i < j) i = j;1885 }1886 1887 b = xrealloc(b, i + 1); 1992 if (i < j) 1993 i = j; 1994 } 1995 1888 1996 free(fmt); 1889 1997 nvfree(v); 1998 b = xrealloc(b, i + 1); 1890 1999 b[i] = '\0'; 1891 2000 return b; 1892 2001 } 1893 2002 1894 /* common substitution routine 1895 * replace (nm) substring of (src) that match (n) with (repl), store 1896 * result into (dest), return number of substitutions. If nm=0, replace 1897 * all matches. If src or dst is NULL, use $0. If ex=TRUE, enable 1898 * subexpression matching (\1-\9) 2003 /* Common substitution routine. 2004 * Replace (nm)'th substring of (src) that matches (rn) with (repl), 2005 * store result into (dest), return number of substitutions. 2006 * If nm = 0, replace all matches. 2007 * If src or dst is NULL, use $0. 2008 * If subexp != 0, enable subexpression matching (\1-\9). 1899 2009 */ 1900 static int awk_sub(node *rn, const char *repl, int nm, var *src, var *dest, int ex) 1901 { 1902 char *ds = NULL; 1903 const char *s; 2010 static int awk_sub(node *rn, const char *repl, int nm, var *src, var *dest, int subexp) 2011 { 2012 char *resbuf; 1904 2013 const char *sp; 1905 int c, i, j, di, rl, so, eo, nbs, n, dssize; 2014 int match_no, residx, replen, resbufsize; 2015 int regexec_flags; 1906 2016 regmatch_t pmatch[10]; 1907 regex_t sreg, *re; 1908 1909 re = as_regex(rn, &sreg); 1910 if (!src) src = intvar[F0]; 1911 if (!dest) dest = intvar[F0]; 1912 1913 i = di = 0; 1914 sp = getvar_s(src); 1915 rl = strlen(repl); 1916 while (regexec(re, sp, 10, pmatch, sp==getvar_s(src) ? 0 : REG_NOTBOL) == 0) { 1917 so = pmatch[0].rm_so; 1918 eo = pmatch[0].rm_eo; 1919 1920 qrealloc(&ds, di + eo + rl, &dssize); 1921 memcpy(ds + di, sp, eo); 1922 di += eo; 1923 if (++i >= nm) { 2017 regex_t sreg, *regex; 2018 2019 resbuf = NULL; 2020 residx = 0; 2021 match_no = 0; 2022 regexec_flags = 0; 2023 regex = as_regex(rn, &sreg); 2024 sp = getvar_s(src ? src : intvar[F0]); 2025 replen = strlen(repl); 2026 while (regexec(regex, sp, 10, pmatch, regexec_flags) == 0) { 2027 int so = pmatch[0].rm_so; 2028 int eo = pmatch[0].rm_eo; 2029 2030 //bb_error_msg("match %u: [%u,%u] '%s'%p", match_no+1, so, eo, sp,sp); 2031 resbuf = qrealloc(resbuf, residx + eo + replen, &resbufsize); 2032 memcpy(resbuf + residx, sp, eo); 2033 residx += eo; 2034 if (++match_no >= nm) { 2035 const char *s; 2036 int nbs; 2037 1924 2038 /* replace */ 1925 di-= (eo - so);2039 residx -= (eo - so); 1926 2040 nbs = 0; 1927 2041 for (s = repl; *s; s++) { 1928 ds[di++] = c= *s;2042 char c = resbuf[residx++] = *s; 1929 2043 if (c == '\\') { 1930 2044 nbs++; 1931 2045 continue; 1932 2046 } 1933 if (c == '&' || (ex && c >= '0' && c <= '9')) { 1934 di -= ((nbs + 3) >> 1); 2047 if (c == '&' || (subexp && c >= '0' && c <= '9')) { 2048 int j; 2049 residx -= ((nbs + 3) >> 1); 1935 2050 j = 0; 1936 2051 if (c != '&') { … … 1939 2054 } 1940 2055 if (nbs % 2) { 1941 ds[di++] = c;2056 resbuf[residx++] = c; 1942 2057 } else { 1943 n = pmatch[j].rm_eo - pmatch[j].rm_so;1944 qrealloc(&ds, di + rl + n, &dssize);1945 memcpy( ds + di, sp + pmatch[j].rm_so, n);1946 di+= n;2058 int n = pmatch[j].rm_eo - pmatch[j].rm_so; 2059 resbuf = qrealloc(resbuf, residx + replen + n, &resbufsize); 2060 memcpy(resbuf + residx, sp + pmatch[j].rm_so, n); 2061 residx += n; 1947 2062 } 1948 2063 } … … 1951 2066 } 1952 2067 2068 regexec_flags = REG_NOTBOL; 1953 2069 sp += eo; 1954 if (i == nm) break; 2070 if (match_no == nm) 2071 break; 1955 2072 if (eo == so) { 1956 ds[di] = *sp++; 1957 if (!ds[di++]) break; 1958 } 1959 } 1960 1961 qrealloc(&ds, di + strlen(sp), &dssize); 1962 strcpy(ds + di, sp); 1963 setvar_p(dest, ds); 1964 if (re == &sreg) regfree(re); 1965 return i; 1966 } 1967 1968 static var *exec_builtin(node *op, var *res) 2073 /* Empty match (e.g. "b*" will match anywhere). 2074 * Advance by one char. */ 2075 //BUG (bug 1333): 2076 //gsub(/\<b*/,"") on "abc" will reach this point, advance to "bc" 2077 //... and will erroneously match "b" even though it is NOT at the word start. 2078 //we need REG_NOTBOW but it does not exist... 2079 //TODO: if EXTRA_COMPAT=y, use GNU matching and re_search, 2080 //it should be able to do it correctly. 2081 /* Subtle: this is safe only because 2082 * qrealloc allocated at least one extra byte */ 2083 resbuf[residx] = *sp; 2084 if (*sp == '\0') 2085 goto ret; 2086 sp++; 2087 residx++; 2088 } 2089 } 2090 2091 resbuf = qrealloc(resbuf, residx + strlen(sp), &resbufsize); 2092 strcpy(resbuf + residx, sp); 2093 ret: 2094 //bb_error_msg("end sp:'%s'%p", sp,sp); 2095 setvar_p(dest ? dest : intvar[F0], resbuf); 2096 if (regex == &sreg) 2097 regfree(regex); 2098 return match_no; 2099 } 2100 2101 static NOINLINE int do_mktime(const char *ds) 2102 { 2103 struct tm then; 2104 int count; 2105 2106 /*memset(&then, 0, sizeof(then)); - not needed */ 2107 then.tm_isdst = -1; /* default is unknown */ 2108 2109 /* manpage of mktime says these fields are ints, 2110 * so we can sscanf stuff directly into them */ 2111 count = sscanf(ds, "%u %u %u %u %u %u %d", 2112 &then.tm_year, &then.tm_mon, &then.tm_mday, 2113 &then.tm_hour, &then.tm_min, &then.tm_sec, 2114 &then.tm_isdst); 2115 2116 if (count < 6 2117 || (unsigned)then.tm_mon < 1 2118 || (unsigned)then.tm_year < 1900 2119 ) { 2120 return -1; 2121 } 2122 2123 then.tm_mon -= 1; 2124 then.tm_year -= 1900; 2125 2126 return mktime(&then); 2127 } 2128 2129 static NOINLINE var *exec_builtin(node *op, var *res) 1969 2130 { 1970 2131 #define tspl (G.exec_builtin__tspl) 1971 2132 1972 int (*to_xxx)(int);1973 2133 var *tv; 1974 2134 node *an[4]; … … 1981 2141 int nargs; 1982 2142 time_t tt; 1983 char *s, *s1;1984 2143 int i, l, ll, n; 1985 2144 … … 1991 2150 for (i = 0; i < 4 && op; i++) { 1992 2151 an[i] = nextarg(&op); 1993 if (isr & 0x09000000) av[i] = evaluate(an[i], &tv[i]); 1994 if (isr & 0x08000000) as[i] = getvar_s(av[i]); 2152 if (isr & 0x09000000) 2153 av[i] = evaluate(an[i], &tv[i]); 2154 if (isr & 0x08000000) 2155 as[i] = getvar_s(av[i]); 1995 2156 isr >>= 1; 1996 2157 } 1997 2158 1998 2159 nargs = i; 1999 if ( nargs < (info >> 30))2160 if ((uint32_t)nargs < (info >> 30)) 2000 2161 syntax_error(EMSG_TOO_FEW_ARGS); 2001 2162 2002 switch (info & OPNMASK) { 2163 info &= OPNMASK; 2164 switch (info) { 2003 2165 2004 2166 case B_a2: 2005 #if ENABLE_FEATURE_AWK_MATH 2006 setvar_i(res, atan2(getvar_i(av[i]), getvar_i(av[1]))); 2007 #else 2008 syntax_error(EMSG_NO_MATH); 2009 #endif 2167 if (ENABLE_FEATURE_AWK_LIBM) 2168 setvar_i(res, atan2(getvar_i(av[0]), getvar_i(av[1]))); 2169 else 2170 syntax_error(EMSG_NO_MATH); 2010 2171 break; 2011 2172 2012 case B_sp: 2173 case B_sp: { 2174 char *s, *s1; 2175 2013 2176 if (nargs > 2) { 2014 2177 spl = (an[2]->info & OPCLSMASK) == OC_REGEXP ? … … 2021 2184 s1 = s; 2022 2185 clear_array(iamarray(av[1])); 2023 for (i =1; i<=n; i++)2024 setari_u(av[1], i, nextword(&s 1));2025 free(s );2186 for (i = 1; i <= n; i++) 2187 setari_u(av[1], i, nextword(&s)); 2188 free(s1); 2026 2189 setvar_i(res, n); 2027 2190 break; 2028 2029 case B_ss: 2191 } 2192 2193 case B_ss: { 2194 char *s; 2195 2030 2196 l = strlen(as[0]); 2031 2197 i = getvar_i(av[1]) - 1; 2032 if (i > l) i = l; 2033 if (i < 0) i = 0; 2198 if (i > l) 2199 i = l; 2200 if (i < 0) 2201 i = 0; 2034 2202 n = (nargs > 2) ? getvar_i(av[2]) : l-i; 2035 if (n < 0) n = 0; 2036 s = xmalloc(n+1); 2037 strncpy(s, as[0]+i, n); 2038 s[n] = '\0'; 2203 if (n < 0) 2204 n = 0; 2205 s = xstrndup(as[0]+i, n); 2039 2206 setvar_p(res, s); 2040 2207 break; 2041 2208 } 2209 2210 /* Bitwise ops must assume that operands are unsigned. GNU Awk 3.1.5: 2211 * awk '{ print or(-1,1) }' gives "4.29497e+09", not "-2.xxxe+09" */ 2042 2212 case B_an: 2043 setvar_i(res, (long)getvar_i(av[0]) & (long)getvar_i(av[1]));2213 setvar_i(res, getvar_i_int(av[0]) & getvar_i_int(av[1])); 2044 2214 break; 2045 2215 2046 2216 case B_co: 2047 setvar_i(res, ~ (long)getvar_i(av[0]));2217 setvar_i(res, ~getvar_i_int(av[0])); 2048 2218 break; 2049 2219 2050 2220 case B_ls: 2051 setvar_i(res, (long)getvar_i(av[0]) << (long)getvar_i(av[1]));2221 setvar_i(res, getvar_i_int(av[0]) << getvar_i_int(av[1])); 2052 2222 break; 2053 2223 2054 2224 case B_or: 2055 setvar_i(res, (long)getvar_i(av[0]) | (long)getvar_i(av[1]));2225 setvar_i(res, getvar_i_int(av[0]) | getvar_i_int(av[1])); 2056 2226 break; 2057 2227 2058 2228 case B_rs: 2059 setvar_i(res, (long)((unsigned long)getvar_i(av[0]) >> (unsigned long)getvar_i(av[1])));2229 setvar_i(res, getvar_i_int(av[0]) >> getvar_i_int(av[1])); 2060 2230 break; 2061 2231 2062 2232 case B_xo: 2063 setvar_i(res, (long)getvar_i(av[0]) ^ (long)getvar_i(av[1]));2233 setvar_i(res, getvar_i_int(av[0]) ^ getvar_i_int(av[1])); 2064 2234 break; 2065 2235 2066 2236 case B_lo: 2067 to_xxx = tolower; 2068 goto lo_cont; 2069 2070 case B_up: 2071 to_xxx = toupper; 2072 lo_cont: 2237 case B_up: { 2238 char *s, *s1; 2073 2239 s1 = s = xstrdup(as[0]); 2074 2240 while (*s1) { 2075 *s1 = (*to_xxx)(*s1); 2241 //*s1 = (info == B_up) ? toupper(*s1) : tolower(*s1); 2242 if ((unsigned char)((*s1 | 0x20) - 'a') <= ('z' - 'a')) 2243 *s1 = (info == B_up) ? (*s1 & 0xdf) : (*s1 | 0x20); 2076 2244 s1++; 2077 2245 } 2078 2246 setvar_p(res, s); 2079 2247 break; 2248 } 2080 2249 2081 2250 case B_ix: … … 2085 2254 if (ll > 0 && l >= 0) { 2086 2255 if (!icase) { 2087 s = strstr(as[0], as[1]); 2088 if (s) n = (s - as[0]) + 1; 2256 char *s = strstr(as[0], as[1]); 2257 if (s) 2258 n = (s - as[0]) + 1; 2089 2259 } else { 2090 2260 /* this piece of code is terribly slow and 2091 2261 * really should be rewritten 2092 2262 */ 2093 for (i =0; i<=l; i++) {2263 for (i = 0; i <= l; i++) { 2094 2264 if (strncasecmp(as[0]+i, as[1], ll) == 0) { 2095 2265 n = i+1; … … 2115 2285 break; 2116 2286 2287 case B_mt: 2288 setvar_i(res, do_mktime(as[0])); 2289 break; 2290 2117 2291 case B_ma: 2118 2292 re = as_regex(an[1], &sreg); … … 2128 2302 setvar_i(newvar("RLENGTH"), pmatch[0].rm_eo - pmatch[0].rm_so); 2129 2303 setvar_i(res, pmatch[0].rm_so); 2130 if (re == &sreg) regfree(re); 2304 if (re == &sreg) 2305 regfree(re); 2131 2306 break; 2132 2307 … … 2161 2336 /* seed is initialized to 1 */ 2162 2337 #define seed (G.evaluate__seed) 2163 #define sreg (G.evaluate__sreg) 2164 2165 node *op1; 2338 #define sreg (G.evaluate__sreg) 2339 2166 2340 var *v1; 2167 union {2168 var *v;2169 const char *s;2170 double d;2171 int i;2172 } L, R;2173 uint32_t opinfo;2174 int opn;2175 union {2176 char *s;2177 rstream *rsm;2178 FILE *F;2179 var *v;2180 regex_t *re;2181 uint32_t info;2182 } X;2183 2341 2184 2342 if (!op) 2185 2343 return setvar_s(res, NULL); 2186 2344 2345 debug_printf_eval("entered %s()\n", __func__); 2346 2187 2347 v1 = nvalloc(2); 2188 2348 2189 2349 while (op) { 2350 struct { 2351 var *v; 2352 const char *s; 2353 } L = L; /* for compiler */ 2354 struct { 2355 var *v; 2356 const char *s; 2357 } R = R; 2358 double L_d = L_d; 2359 uint32_t opinfo; 2360 int opn; 2361 node *op1; 2362 2190 2363 opinfo = op->info; 2191 2364 opn = (opinfo & OPNMASK); 2192 2365 g_lineno = op->lineno; 2366 op1 = op->l.n; 2367 debug_printf_eval("opinfo:%08x opn:%08x\n", opinfo, opn); 2193 2368 2194 2369 /* execute inevitable things */ 2195 op1 = op->l.n; 2196 if (opinfo & OF_RES1) X.v = L.v = evaluate(op1, v1); 2197 if (opinfo & OF_RES2) R.v = evaluate(op->r.n, v1+1); 2198 if (opinfo & OF_STR1) L.s = getvar_s(L.v); 2199 if (opinfo & OF_STR2) R.s = getvar_s(R.v); 2200 if (opinfo & OF_NUM1) L.d = getvar_i(L.v); 2201 2370 if (opinfo & OF_RES1) 2371 L.v = evaluate(op1, v1); 2372 if (opinfo & OF_RES2) 2373 R.v = evaluate(op->r.n, v1+1); 2374 if (opinfo & OF_STR1) { 2375 L.s = getvar_s(L.v); 2376 debug_printf_eval("L.s:'%s'\n", L.s); 2377 } 2378 if (opinfo & OF_STR2) { 2379 R.s = getvar_s(R.v); 2380 debug_printf_eval("R.s:'%s'\n", R.s); 2381 } 2382 if (opinfo & OF_NUM1) { 2383 L_d = getvar_i(L.v); 2384 debug_printf_eval("L_d:%f\n", L_d); 2385 } 2386 2387 debug_printf_eval("switch(0x%x)\n", XC(opinfo & OPCLSMASK)); 2202 2388 switch (XC(opinfo & OPCLSMASK)) { 2203 2389 … … 2212 2398 if (ptest(op1->r.n)) 2213 2399 op->info &= ~OF_CHECKED; 2214 2215 2400 op = op->a.n; 2216 2401 } else { … … 2218 2403 } 2219 2404 } else { 2220 op = (ptest(op1)) ? op->a.n : op->r.n;2405 op = ptest(op1) ? op->a.n : op->r.n; 2221 2406 } 2222 2407 break; … … 2242 2427 2243 2428 case XC( OC_PRINT ): 2244 case XC( OC_PRINTF ): 2245 X.F = stdout; 2429 case XC( OC_PRINTF ): { 2430 FILE *F = stdout; 2431 2246 2432 if (op->r.n) { 2247 X.rsm = newfile(R.s);2248 if (! X.rsm->F) {2433 rstream *rsm = newfile(R.s); 2434 if (!rsm->F) { 2249 2435 if (opn == '|') { 2250 X.rsm->F = popen(R.s, "w");2251 if ( X.rsm->F == NULL)2436 rsm->F = popen(R.s, "w"); 2437 if (rsm->F == NULL) 2252 2438 bb_perror_msg_and_die("popen"); 2253 X.rsm->is_pipe = 1;2439 rsm->is_pipe = 1; 2254 2440 } else { 2255 X.rsm->F = xfopen(R.s, opn=='w' ? "w" : "a");2441 rsm->F = xfopen(R.s, opn=='w' ? "w" : "a"); 2256 2442 } 2257 2443 } 2258 X.F = X.rsm->F;2444 F = rsm->F; 2259 2445 } 2260 2446 2261 2447 if ((opinfo & OPCLSMASK) == OC_PRINT) { 2262 2448 if (!op1) { 2263 fputs(getvar_s(intvar[F0]), X.F);2449 fputs(getvar_s(intvar[F0]), F); 2264 2450 } else { 2265 2451 while (op1) { 2266 L.v = evaluate(nextarg(&op1), v1);2267 if ( L.v->type & VF_NUMBER) {2452 var *v = evaluate(nextarg(&op1), v1); 2453 if (v->type & VF_NUMBER) { 2268 2454 fmt_num(g_buf, MAXVARFMT, getvar_s(intvar[OFMT]), 2269 getvar_i( L.v), TRUE);2270 fputs(g_buf, X.F);2455 getvar_i(v), TRUE); 2456 fputs(g_buf, F); 2271 2457 } else { 2272 fputs(getvar_s( L.v), X.F);2458 fputs(getvar_s(v), F); 2273 2459 } 2274 2460 2275 if (op1) fputs(getvar_s(intvar[OFS]), X.F); 2461 if (op1) 2462 fputs(getvar_s(intvar[OFS]), F); 2276 2463 } 2277 2464 } 2278 fputs(getvar_s(intvar[ORS]), X.F);2465 fputs(getvar_s(intvar[ORS]), F); 2279 2466 2280 2467 } else { /* OC_PRINTF */ 2281 L.s = awk_printf(op1); 2282 fputs(L.s, X.F); 2283 free((char*)L.s); 2284 } 2285 fflush(X.F); 2286 break; 2287 2288 case XC( OC_DELETE ): 2289 X.info = op1->info & OPCLSMASK; 2290 if (X.info == OC_VAR) { 2291 R.v = op1->l.v; 2292 } else if (X.info == OC_FNARG) { 2293 R.v = &fnargs[op1->l.i]; 2468 char *s = awk_printf(op1); 2469 fputs(s, F); 2470 free(s); 2471 } 2472 fflush(F); 2473 break; 2474 } 2475 2476 case XC( OC_DELETE ): { 2477 uint32_t info = op1->info & OPCLSMASK; 2478 var *v; 2479 2480 if (info == OC_VAR) { 2481 v = op1->l.v; 2482 } else if (info == OC_FNARG) { 2483 v = &fnargs[op1->l.aidx]; 2294 2484 } else { 2295 2485 syntax_error(EMSG_NOT_ARRAY); … … 2297 2487 2298 2488 if (op1->r.n) { 2489 const char *s; 2299 2490 clrvar(L.v); 2300 L.s = getvar_s(evaluate(op1->r.n, v1));2301 hash_remove(iamarray( R.v), L.s);2491 s = getvar_s(evaluate(op1->r.n, v1)); 2492 hash_remove(iamarray(v), s); 2302 2493 } else { 2303 clear_array(iamarray(R.v)); 2304 } 2305 break; 2494 clear_array(iamarray(v)); 2495 } 2496 break; 2497 } 2306 2498 2307 2499 case XC( OC_NEWSOURCE ): 2308 g_progname = op->l. s;2500 g_progname = op->l.new_progname; 2309 2501 break; 2310 2502 … … 2322 2514 2323 2515 case XC( OC_EXIT ): 2324 awk_exit(L .d);2516 awk_exit(L_d); 2325 2517 2326 2518 /* -- recursive node type -- */ … … 2333 2525 2334 2526 case XC( OC_FNARG ): 2335 L.v = &fnargs[op->l. i];2527 L.v = &fnargs[op->l.aidx]; 2336 2528 v_cont: 2337 2529 res = op->r.n ? findvar(iamarray(L.v), R.s) : L.v; … … 2350 2542 op1 = op->r.n; 2351 2543 re_cont: 2352 X.re = as_regex(op1, &sreg); 2353 R.i = regexec(X.re, L.s, 0, NULL, 0); 2354 if (X.re == &sreg) regfree(X.re); 2355 setvar_i(res, (R.i == 0 ? 1 : 0) ^ (opn == '!' ? 1 : 0)); 2544 { 2545 regex_t *re = as_regex(op1, &sreg); 2546 int i = regexec(re, L.s, 0, NULL, 0); 2547 if (re == &sreg) 2548 regfree(re); 2549 setvar_i(res, (i == 0) ^ (opn == '!')); 2550 } 2356 2551 break; 2357 2552 2358 2553 case XC( OC_MOVE ): 2554 debug_printf_eval("MOVE\n"); 2359 2555 /* if source is a temporary string, jusk relink it to dest */ 2360 if (R.v == v1+1 && R.v->string) { 2361 res = setvar_p(L.v, R.v->string); 2362 R.v->string = NULL; 2363 } else { 2556 //Disabled: if R.v is numeric but happens to have cached R.v->string, 2557 //then L.v ends up being a string, which is wrong 2558 // if (R.v == v1+1 && R.v->string) { 2559 // res = setvar_p(L.v, R.v->string); 2560 // R.v->string = NULL; 2561 // } else { 2364 2562 res = copyvar(L.v, R.v); 2365 }2563 // } 2366 2564 break; 2367 2565 … … 2372 2570 break; 2373 2571 2374 case XC( OC_FUNC ): 2572 case XC( OC_FUNC ): { 2573 var *vbeg, *v; 2574 const char *sv_progname; 2575 2375 2576 if (!op->r.f->body.first) 2376 2577 syntax_error(EMSG_UNDEF_FUNC); 2377 2578 2378 X.v = R.v = nvalloc(op->r.f->nargs+1);2579 vbeg = v = nvalloc(op->r.f->nargs + 1); 2379 2580 while (op1) { 2380 L.v= evaluate(nextarg(&op1), v1);2381 copyvar( R.v, L.v);2382 R.v->type |= VF_CHILD;2383 R.v->x.parent = L.v;2384 if (++ R.v - X.v>= op->r.f->nargs)2581 var *arg = evaluate(nextarg(&op1), v1); 2582 copyvar(v, arg); 2583 v->type |= VF_CHILD; 2584 v->x.parent = arg; 2585 if (++v - vbeg >= op->r.f->nargs) 2385 2586 break; 2386 2587 } 2387 2588 2388 R.v = fnargs;2389 fnargs = X.v;2390 2391 L.s = g_progname; 2589 v = fnargs; 2590 fnargs = vbeg; 2591 sv_progname = g_progname; 2592 2392 2593 res = evaluate(op->r.f->body.first, res); 2393 g_progname = L.s; 2394 2594 2595 g_progname = sv_progname; 2395 2596 nvfree(fnargs); 2396 fnargs = R.v; 2397 break; 2597 fnargs = v; 2598 2599 break; 2600 } 2398 2601 2399 2602 case XC( OC_GETLINE ): 2400 case XC( OC_PGETLINE ): 2603 case XC( OC_PGETLINE ): { 2604 rstream *rsm; 2605 int i; 2606 2401 2607 if (op1) { 2402 X.rsm = newfile(L.s);2403 if (! X.rsm->F) {2608 rsm = newfile(L.s); 2609 if (!rsm->F) { 2404 2610 if ((opinfo & OPCLSMASK) == OC_PGETLINE) { 2405 X.rsm->F = popen(L.s, "r");2406 X.rsm->is_pipe = TRUE;2611 rsm->F = popen(L.s, "r"); 2612 rsm->is_pipe = TRUE; 2407 2613 } else { 2408 X.rsm->F = fopen(L.s, "r");/* not xfopen! */2614 rsm->F = fopen_for_read(L.s); /* not xfopen! */ 2409 2615 } 2410 2616 } 2411 2617 } else { 2412 if (!iF) iF = next_input_file(); 2413 X.rsm = iF; 2414 } 2415 2416 if (!X.rsm->F) { 2618 if (!iF) 2619 iF = next_input_file(); 2620 rsm = iF; 2621 } 2622 2623 if (!rsm->F) { 2417 2624 setvar_i(intvar[ERRNO], errno); 2418 2625 setvar_i(res, -1); … … 2423 2630 R.v = intvar[F0]; 2424 2631 2425 L.i = awk_getline(X.rsm, R.v); 2426 if (L.i > 0) { 2427 if (!op1) { 2428 incvar(intvar[FNR]); 2429 incvar(intvar[NR]); 2632 i = awk_getline(rsm, R.v); 2633 if (i > 0 && !op1) { 2634 incvar(intvar[FNR]); 2635 incvar(intvar[NR]); 2636 } 2637 setvar_i(res, i); 2638 break; 2639 } 2640 2641 /* simple builtins */ 2642 case XC( OC_FBLTIN ): { 2643 double R_d = R_d; /* for compiler */ 2644 2645 switch (opn) { 2646 case F_in: 2647 R_d = (int)L_d; 2648 break; 2649 2650 case F_rn: 2651 R_d = (double)rand() / (double)RAND_MAX; 2652 break; 2653 2654 case F_co: 2655 if (ENABLE_FEATURE_AWK_LIBM) { 2656 R_d = cos(L_d); 2657 break; 2430 2658 } 2431 }2432 setvar_i(res, L.i);2433 break;2434 2435 /* simple builtins */2436 case XC( OC_FBLTIN ):2437 switch (opn) {2438 2439 case F_in:2440 R.d = (int)L.d;2441 break;2442 2443 case F_rn:2444 R.d = (double)rand() / (double)RAND_MAX;2445 break;2446 #if ENABLE_FEATURE_AWK_MATH2447 case F_co:2448 R.d = cos(L.d);2449 break;2450 2659 2451 2660 case F_ex: 2452 R.d = exp(L.d); 2453 break; 2661 if (ENABLE_FEATURE_AWK_LIBM) { 2662 R_d = exp(L_d); 2663 break; 2664 } 2454 2665 2455 2666 case F_lg: 2456 R.d = log(L.d); 2457 break; 2667 if (ENABLE_FEATURE_AWK_LIBM) { 2668 R_d = log(L_d); 2669 break; 2670 } 2458 2671 2459 2672 case F_si: 2460 R.d = sin(L.d); 2461 break; 2673 if (ENABLE_FEATURE_AWK_LIBM) { 2674 R_d = sin(L_d); 2675 break; 2676 } 2462 2677 2463 2678 case F_sq: 2464 R.d = sqrt(L.d); 2465 break; 2466 #else 2467 case F_co: 2468 case F_ex: 2469 case F_lg: 2470 case F_si: 2471 case F_sq: 2679 if (ENABLE_FEATURE_AWK_LIBM) { 2680 R_d = sqrt(L_d); 2681 break; 2682 } 2683 2472 2684 syntax_error(EMSG_NO_MATH); 2473 2685 break; 2474 #endif 2686 2475 2687 case F_sr: 2476 R .d = (double)seed;2477 seed = op1 ? (unsigned)L .d : (unsigned)time(NULL);2688 R_d = (double)seed; 2689 seed = op1 ? (unsigned)L_d : (unsigned)time(NULL); 2478 2690 srand(seed); 2479 2691 break; 2480 2692 2481 2693 case F_ti: 2482 R .d = time(NULL);2694 R_d = time(NULL); 2483 2695 break; 2484 2696 … … 2486 2698 if (!op1) 2487 2699 L.s = getvar_s(intvar[F0]); 2488 R .d = strlen(L.s);2700 R_d = strlen(L.s); 2489 2701 break; 2490 2702 2491 2703 case F_sy: 2492 fflush (NULL);2493 R .d = (ENABLE_FEATURE_ALLOW_EXEC && L.s && *L.s)2704 fflush_all(); 2705 R_d = (ENABLE_FEATURE_ALLOW_EXEC && L.s && *L.s) 2494 2706 ? (system(L.s) >> 8) : 0; 2495 2707 break; 2496 2708 2497 2709 case F_ff: 2498 if (!op1) 2710 if (!op1) { 2499 2711 fflush(stdout); 2500 else { 2501 if (L.s && *L.s) { 2502 X.rsm = newfile(L.s); 2503 fflush(X.rsm->F); 2504 } else { 2505 fflush(NULL); 2506 } 2712 } else if (L.s && *L.s) { 2713 rstream *rsm = newfile(L.s); 2714 fflush(rsm->F); 2715 } else { 2716 fflush_all(); 2507 2717 } 2508 2718 break; 2509 2719 2510 case F_cl: 2511 X.rsm = (rstream *)hash_search(fdhash, L.s); 2512 if (X.rsm) { 2513 R.i = X.rsm->is_pipe ? pclose(X.rsm->F) : fclose(X.rsm->F); 2514 free(X.rsm->buffer); 2720 case F_cl: { 2721 rstream *rsm; 2722 int err = 0; 2723 rsm = (rstream *)hash_search(fdhash, L.s); 2724 debug_printf_eval("OC_FBLTIN F_cl rsm:%p\n", rsm); 2725 if (rsm) { 2726 debug_printf_eval("OC_FBLTIN F_cl " 2727 "rsm->is_pipe:%d, ->F:%p\n", 2728 rsm->is_pipe, rsm->F); 2729 /* Can be NULL if open failed. Example: 2730 * getline line <"doesnt_exist"; 2731 * close("doesnt_exist"); <--- here rsm->F is NULL 2732 */ 2733 if (rsm->F) 2734 err = rsm->is_pipe ? pclose(rsm->F) : fclose(rsm->F); 2735 free(rsm->buffer); 2515 2736 hash_remove(fdhash, L.s); 2516 2737 } 2517 if ( R.i != 0)2738 if (err) 2518 2739 setvar_i(intvar[ERRNO], errno); 2519 R .d = (double)R.i;2740 R_d = (double)err; 2520 2741 break; 2521 2742 } 2522 setvar_i(res, R.d); 2523 break; 2743 } /* switch */ 2744 setvar_i(res, R_d); 2745 break; 2746 } 2524 2747 2525 2748 case XC( OC_BUILTIN ): … … 2531 2754 break; 2532 2755 2533 case XC( OC_UNARY ): 2534 X.v = R.v; 2535 L.d = R.d = getvar_i(R.v); 2756 case XC( OC_UNARY ): { 2757 double Ld, R_d; 2758 2759 Ld = R_d = getvar_i(R.v); 2536 2760 switch (opn) { 2537 2761 case 'P': 2538 L .d = ++R.d;2762 Ld = ++R_d; 2539 2763 goto r_op_change; 2540 2764 case 'p': 2541 R .d++;2765 R_d++; 2542 2766 goto r_op_change; 2543 2767 case 'M': 2544 L .d = --R.d;2768 Ld = --R_d; 2545 2769 goto r_op_change; 2546 2770 case 'm': 2547 R.d--; 2548 goto r_op_change; 2771 R_d--; 2772 r_op_change: 2773 setvar_i(R.v, R_d); 2774 break; 2549 2775 case '!': 2550 L .d = istrue(X.v) ? 0 : 1;2776 Ld = !istrue(R.v); 2551 2777 break; 2552 2778 case '-': 2553 L .d = -R.d;2779 Ld = -R_d; 2554 2780 break; 2555 r_op_change: 2556 setvar_i(X.v, R.d); 2557 } 2558 setvar_i(res, L.d); 2559 break; 2560 2561 case XC( OC_FIELD ): 2562 R.i = (int)getvar_i(R.v); 2563 if (R.i == 0) { 2781 } 2782 setvar_i(res, Ld); 2783 break; 2784 } 2785 2786 case XC( OC_FIELD ): { 2787 int i = (int)getvar_i(R.v); 2788 if (i == 0) { 2564 2789 res = intvar[F0]; 2565 2790 } else { 2566 2791 split_f0(); 2567 if (R.i > nfields) 2568 fsrealloc(R.i); 2569 res = &Fields[R.i - 1]; 2570 } 2571 break; 2792 if (i > nfields) 2793 fsrealloc(i); 2794 res = &Fields[i - 1]; 2795 } 2796 break; 2797 } 2572 2798 2573 2799 /* concatenation (" ") and index joining (",") */ 2574 2800 case XC( OC_CONCAT ): 2575 case XC( OC_COMMA ): 2576 opn = strlen(L.s) + strlen(R.s) + 2; 2577 X.s = xmalloc(opn); 2578 strcpy(X.s, L.s); 2579 if ((opinfo & OPCLSMASK) == OC_COMMA) { 2580 L.s = getvar_s(intvar[SUBSEP]); 2581 X.s = xrealloc(X.s, opn + strlen(L.s)); 2582 strcat(X.s, L.s); 2583 } 2584 strcat(X.s, R.s); 2585 setvar_p(res, X.s); 2586 break; 2801 case XC( OC_COMMA ): { 2802 const char *sep = ""; 2803 if ((opinfo & OPCLSMASK) == OC_COMMA) 2804 sep = getvar_s(intvar[SUBSEP]); 2805 setvar_p(res, xasprintf("%s%s%s", L.s, sep, R.s)); 2806 break; 2807 } 2587 2808 2588 2809 case XC( OC_LAND ): … … 2595 2816 2596 2817 case XC( OC_BINARY ): 2597 case XC( OC_REPLACE ): 2598 R.d = getvar_i(R.v); 2818 case XC( OC_REPLACE ): { 2819 double R_d = getvar_i(R.v); 2820 debug_printf_eval("BINARY/REPLACE: R_d:%f opn:%c\n", R_d, opn); 2599 2821 switch (opn) { 2600 2822 case '+': 2601 L .d += R.d;2823 L_d += R_d; 2602 2824 break; 2603 2825 case '-': 2604 L .d -= R.d;2826 L_d -= R_d; 2605 2827 break; 2606 2828 case '*': 2607 L .d *= R.d;2829 L_d *= R_d; 2608 2830 break; 2609 2831 case '/': 2610 if (R.d == 0) syntax_error(EMSG_DIV_BY_ZERO); 2611 L.d /= R.d; 2832 if (R_d == 0) 2833 syntax_error(EMSG_DIV_BY_ZERO); 2834 L_d /= R_d; 2612 2835 break; 2613 2836 case '&': 2614 #if ENABLE_FEATURE_AWK_MATH 2615 L.d = pow(L.d, R.d); 2616 #else 2617 syntax_error(EMSG_NO_MATH); 2618 #endif 2837 if (ENABLE_FEATURE_AWK_LIBM) 2838 L_d = pow(L_d, R_d); 2839 else 2840 syntax_error(EMSG_NO_MATH); 2619 2841 break; 2620 2842 case '%': 2621 if (R.d == 0) syntax_error(EMSG_DIV_BY_ZERO); 2622 L.d -= (int)(L.d / R.d) * R.d; 2843 if (R_d == 0) 2844 syntax_error(EMSG_DIV_BY_ZERO); 2845 L_d -= (int)(L_d / R_d) * R_d; 2623 2846 break; 2624 2847 } 2625 res = setvar_i(((opinfo & OPCLSMASK) == OC_BINARY) ? res : X.v, L.d); 2626 break; 2627 2628 case XC( OC_COMPARE ): 2848 debug_printf_eval("BINARY/REPLACE result:%f\n", L_d); 2849 res = setvar_i(((opinfo & OPCLSMASK) == OC_BINARY) ? res : L.v, L_d); 2850 break; 2851 } 2852 2853 case XC( OC_COMPARE ): { 2854 int i = i; /* for compiler */ 2855 double Ld; 2856 2629 2857 if (is_numeric(L.v) && is_numeric(R.v)) { 2630 L .d = getvar_i(L.v) - getvar_i(R.v);2858 Ld = getvar_i(L.v) - getvar_i(R.v); 2631 2859 } else { 2632 L.s= getvar_s(L.v);2633 R.s= getvar_s(R.v);2634 L .d = icase ? strcasecmp(L.s, R.s) : strcmp(L.s, R.s);2860 const char *l = getvar_s(L.v); 2861 const char *r = getvar_s(R.v); 2862 Ld = icase ? strcasecmp(l, r) : strcmp(l, r); 2635 2863 } 2636 2864 switch (opn & 0xfe) { 2637 2865 case 0: 2638 R.i = (L.d > 0);2866 i = (Ld > 0); 2639 2867 break; 2640 2868 case 2: 2641 R.i = (L.d >= 0);2869 i = (Ld >= 0); 2642 2870 break; 2643 2871 case 4: 2644 R.i = (L.d == 0);2872 i = (Ld == 0); 2645 2873 break; 2646 2874 } 2647 setvar_i(res, (opn & 0x1 ? R.i : !R.i) ? 1 : 0); 2648 break; 2875 setvar_i(res, (i == 0) ^ (opn & 1)); 2876 break; 2877 } 2649 2878 2650 2879 default: … … 2657 2886 if (nextrec) 2658 2887 break; 2659 } 2888 } /* while (op) */ 2889 2660 2890 nvfree(v1); 2891 debug_printf_eval("returning from %s(): %p\n", __func__, res); 2661 2892 return res; 2662 2893 #undef fnargs … … 2699 2930 static int is_assignment(const char *expr) 2700 2931 { 2701 char *exprc, *s, *s0, *s1; 2932 char *exprc, *val, *s, *s1; 2933 2934 if (!isalnum_(*expr) || (val = strchr(expr, '=')) == NULL) { 2935 return FALSE; 2936 } 2702 2937 2703 2938 exprc = xstrdup(expr); 2704 if (!isalnum_(*exprc) || (s = strchr(exprc, '=')) == NULL) { 2705 free(exprc); 2706 return FALSE; 2707 } 2708 2709 *(s++) = '\0'; 2710 s0 = s1 = s; 2711 while (*s) 2712 *(s1++) = nextchar(&s); 2713 2714 *s1 = '\0'; 2715 setvar_u(newvar(exprc), s0); 2939 val = exprc + (val - expr); 2940 *val++ = '\0'; 2941 2942 s = s1 = val; 2943 while ((*s1 = nextchar(&s)) != '\0') 2944 s1++; 2945 2946 setvar_u(newvar(exprc), val); 2716 2947 free(exprc); 2717 2948 return TRUE; … … 2727 2958 const char *fname, *ind; 2728 2959 2729 if (rsm.F) fclose(rsm.F); 2960 if (rsm.F) 2961 fclose(rsm.F); 2730 2962 rsm.F = NULL; 2731 2963 rsm.pos = rsm.adv = 0; … … 2741 2973 fname = getvar_s(findvar(iamarray(intvar[ARGV]), ind)); 2742 2974 if (fname && *fname && !is_assignment(fname)) 2743 F = afopen(fname, "r");2975 F = xfopen_stdin(fname); 2744 2976 } 2745 2977 } while (!F); … … 2753 2985 } 2754 2986 2755 int awk_main(int argc, char **argv) ;2987 int awk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 2756 2988 int awk_main(int argc, char **argv) 2757 2989 { 2758 2990 unsigned opt; 2759 2991 char *opt_F, *opt_W; 2760 llist_t *opt_v = NULL; 2761 int i, j, flen; 2992 llist_t *list_v = NULL; 2993 llist_t *list_f = NULL; 2994 int i, j; 2762 2995 var *v; 2763 2996 var tv; … … 2817 3050 } 2818 3051 } 2819 opt_complementary = "v:: ";2820 opt = getopt32(argv, "F:v:f:W:", &opt_F, & opt_v, &g_progname, &opt_W);3052 opt_complementary = "v::f::"; /* -v and -f can occur multiple times */ 3053 opt = getopt32(argv, "F:v:f:W:", &opt_F, &list_v, &list_f, &opt_W); 2821 3054 argv += optind; 2822 3055 argc -= optind; 2823 3056 if (opt & 0x1) 2824 3057 setvar_s(intvar[FS], opt_F); // -F 2825 while ( opt_v) { /* -v */2826 if (!is_assignment(llist_pop(& opt_v)))3058 while (list_v) { /* -v */ 3059 if (!is_assignment(llist_pop(&list_v))) 2827 3060 bb_show_usage(); 2828 3061 } 2829 if (opt & 0x4) { // -f 2830 char *s = s; /* die, gcc, die */ 2831 FILE *from_file = afopen(g_progname, "r"); 2832 /* one byte is reserved for some trick in next_token */ 2833 if (fseek(from_file, 0, SEEK_END) == 0) { 2834 flen = ftell(from_file); 2835 s = xmalloc(flen + 4); 2836 fseek(from_file, 0, SEEK_SET); 2837 i = 1 + fread(s + 1, 1, flen, from_file); 2838 } else { 3062 if (list_f) { /* -f */ 3063 do { 3064 char *s = NULL; 3065 FILE *from_file; 3066 3067 g_progname = llist_pop(&list_f); 3068 from_file = xfopen_stdin(g_progname); 3069 /* one byte is reserved for some trick in next_token */ 2839 3070 for (i = j = 1; j > 0; i += j) { 2840 3071 s = xrealloc(s, i + 4096); 2841 3072 j = fread(s + i, 1, 4094, from_file); 2842 3073 } 2843 } 2844 s[i] = '\0'; 2845 fclose(from_file); 2846 parse_program(s + 1); 2847 free(s); 3074 s[i] = '\0'; 3075 fclose(from_file); 3076 parse_program(s + 1); 3077 free(s); 3078 } while (list_f); 3079 argc++; 2848 3080 } else { // no -f: take program from 1st parameter 2849 3081 if (!argc) … … 2851 3083 g_progname = "cmd. line"; 2852 3084 parse_program(*argv++); 2853 argc--;2854 3085 } 2855 3086 if (opt & 0x8) // -W … … 2857 3088 2858 3089 /* fill in ARGV array */ 2859 setvar_i(intvar[ARGC], argc + 1);3090 setvar_i(intvar[ARGC], argc); 2860 3091 setari_u(intvar[ARGV], 0, "awk"); 2861 3092 i = 0; … … 2868 3099 2869 3100 /* input file could already be opened in BEGIN block */ 2870 if (!iF) iF = next_input_file(); 3101 if (!iF) 3102 iF = next_input_file(); 2871 3103 2872 3104 /* passing through input files */ -
branches/2.2.9/mindi-busybox/editors/cmp.c
r1765 r2725 5 5 * Copyright (C) 2000,2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu> 6 6 * 7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.7 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 8 8 */ 9 9 … … 11 11 /* http://www.opengroup.org/onlinepubs/007904975/utilities/cmp.html */ 12 12 13 /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)14 *15 * Original version majorly reworked for SUSv3 compliance, bug fixes, and16 * size optimizations. Changes include:17 * 1) Now correctly distinguishes between errors and actual file differences.18 * 2) Proper handling of '-' args.19 * 3) Actual error checking of i/o.20 * 4) Accept SUSv3 -l option. Note that we use the slightly nicer gnu format21 * in the '-l' case.22 */23 24 13 #include "libbb.h" 25 14 26 static FILE *cmp_xfopen_input(const char *filename)27 {28 FILE *fp;29 30 fp = fopen_or_warn_stdin(filename);31 if (fp)32 return fp;33 xfunc_die(); /* We already output an error message. */34 }35 36 15 static const char fmt_eof[] ALIGN1 = "cmp: EOF on %s\n"; 37 static const char fmt_differ[] ALIGN1 = "%s %s differ: char %"OFF_FMT" d, line %d\n";38 // This fmt_l_opt uses gnu-isms. SUSv3 would be "%.0s%.0s%"OFF_FMT" d%o %o\n"39 static const char fmt_l_opt[] ALIGN1 = "%.0s%.0s%"OFF_FMT" d%3o %3o\n";16 static const char fmt_differ[] ALIGN1 = "%s %s differ: char %"OFF_FMT"u, line %u\n"; 17 // This fmt_l_opt uses gnu-isms. SUSv3 would be "%.0s%.0s%"OFF_FMT"u %o %o\n" 18 static const char fmt_l_opt[] ALIGN1 = "%.0s%.0s%"OFF_FMT"u %3o %3o\n"; 40 19 41 20 static const char opt_chars[] ALIGN1 = "sl"; … … 43 22 #define CMP_OPT_l (1<<1) 44 23 45 int cmp_main(int argc, char **argv) ;46 int cmp_main(int argc , char **argv)24 int cmp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 25 int cmp_main(int argc UNUSED_PARAM, char **argv) 47 26 { 48 27 FILE *fp1, *fp2, *outfile = stdout; 49 28 const char *filename1, *filename2 = "-"; 50 USE_DESKTOP(off_t skip1 = 0, skip2 = 0;) 51 off_t char_pos = 0; 29 off_t skip1 = 0, skip2 = 0, char_pos = 0; 52 30 int line_pos = 1; /* Hopefully won't overflow... */ 53 31 const char *fmt; … … 56 34 int retval = 0; 57 35 58 xfunc_error_retval = 2; /* 1 is returned if files are different. */59 60 36 opt_complementary = "-1" 61 USE_DESKTOP(":?4")62 SKIP_DESKTOP(":?2")37 IF_DESKTOP(":?4") 38 IF_NOT_DESKTOP(":?2") 63 39 ":l--s:s--l"; 64 40 opt = getopt32(argv, opt_chars); … … 66 42 67 43 filename1 = *argv; 68 fp1 = cmp_xfopen_input(filename1);69 70 44 if (*++argv) { 71 45 filename2 = *argv; 72 #if ENABLE_DESKTOP 73 if (*++argv) { 46 if (ENABLE_DESKTOP && *++argv) { 74 47 skip1 = XATOOFF(*argv); 75 48 if (*++argv) { … … 77 50 } 78 51 } 79 #endif80 52 } 81 53 82 fp2 = cmp_xfopen_input(filename2); 54 xfunc_error_retval = 2; /* missing file results in exitcode 2 */ 55 if (opt & CMP_OPT_s) 56 logmode = 0; /* -s suppresses open error messages */ 57 fp1 = xfopen_stdin(filename1); 58 fp2 = xfopen_stdin(filename2); 83 59 if (fp1 == fp2) { /* Paranoia check... stdin == stdin? */ 84 60 /* Note that we don't bother reading stdin. Neither does gnu wc. … … 88 64 return 0; 89 65 } 66 logmode = LOGMODE_STDIO; 90 67 91 68 if (opt & CMP_OPT_l) … … 94 71 fmt = fmt_differ; 95 72 96 #if ENABLE_DESKTOP 97 while (skip1) { getc(fp1); skip1--; }98 while (skip2) { getc(fp2); skip2--; }99 #endif 73 if (ENABLE_DESKTOP) { 74 while (skip1) { getc(fp1); skip1--; } 75 while (skip2) { getc(fp2); skip2--; } 76 } 100 77 do { 101 78 c1 = getc(fp1); … … 119 96 /* There may have been output to stdout (option -l), so 120 97 * make sure we fflush before writing to stderr. */ 121 xfflush_stdout();98 fflush_all(); 122 99 } 123 100 if (!(opt & CMP_OPT_s)) { -
branches/2.2.9/mindi-busybox/editors/diff.c
r1765 r2725 3 3 * Mini diff implementation for busybox, adapted from OpenBSD diff. 4 4 * 5 * Copyright (C) 2010 by Matheus Izvekov <mizvekov@gmail.com> 5 6 * Copyright (C) 2006 by Robert Sullivan <cogito.ergo.cogito@hotmail.com> 6 7 * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com> … … 10 11 * Materiel Command, USAF, under agreement number F39502-99-1-0512. 11 12 * 12 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.13 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 13 14 */ 14 15 #include "libbb.h"16 17 #define FSIZE_MAX 3276818 19 /*20 * Output flags21 */22 #define D_HEADER 1 /* Print a header/footer between files */23 #define D_EMPTY1 2 /* Treat first file as empty (/dev/null) */24 #define D_EMPTY2 4 /* Treat second file as empty (/dev/null) */25 26 /*27 * Status values for print_status() and diffreg() return values28 * Guide:29 * D_SAME - files are the same30 * D_DIFFER - files differ31 * D_BINARY - binary files differ32 * D_COMMON - subdirectory common to both dirs33 * D_ONLY - file only exists in one dir34 * D_MISMATCH1 - path1 a dir, path2 a file35 * D_MISMATCH2 - path1 a file, path2 a dir36 * D_ERROR - error occurred37 * D_SKIPPED1 - skipped path1 as it is a special file38 * D_SKIPPED2 - skipped path2 as it is a special file39 */40 41 #define D_SAME 042 #define D_DIFFER (1<<0)43 #define D_BINARY (1<<1)44 #define D_COMMON (1<<2)45 #define D_ONLY (1<<3)46 #define D_MISMATCH1 (1<<4)47 #define D_MISMATCH2 (1<<5)48 #define D_ERROR (1<<6)49 #define D_SKIPPED1 (1<<7)50 #define D_SKIPPED2 (1<<8)51 52 /* Command line options */53 #define FLAG_a (1<<0)54 #define FLAG_b (1<<1)55 #define FLAG_d (1<<2)56 #define FLAG_i (1<<3)57 #define FLAG_L (1<<4)58 #define FLAG_N (1<<5)59 #define FLAG_q (1<<6)60 #define FLAG_r (1<<7)61 #define FLAG_s (1<<8)62 #define FLAG_S (1<<9)63 #define FLAG_t (1<<10)64 #define FLAG_T (1<<11)65 #define FLAG_U (1<<12)66 #define FLAG_w (1<<13)67 68 #define g_read_buf bb_common_bufsiz169 70 struct cand {71 int x;72 int y;73 int pred;74 };75 76 struct line {77 int serial;78 int value;79 };80 81 /*82 * The following struct is used to record change information83 * doing a "context" or "unified" diff. (see routine "change" to84 * understand the highly mnemonic field names)85 */86 struct context_vec {87 int a; /* start line in old file */88 int b; /* end line in old file */89 int c; /* start line in new file */90 int d; /* end line in new file */91 };92 93 struct globals {94 USE_FEATURE_DIFF_DIR(char **dl;)95 USE_FEATURE_DIFF_DIR(int dl_count;)96 /* This is the default number of lines of context. */97 int context;98 size_t max_context;99 int status;100 char *start;101 const char *label1;102 const char *label2;103 struct line *file[2];104 int *J; /* will be overlaid on class */105 int *class; /* will be overlaid on file[0] */106 int *klist; /* will be overlaid on file[0] after class */107 int *member; /* will be overlaid on file[1] */108 int clen;109 int len[2];110 int pref, suff; /* length of prefix and suffix */111 int slen[2];112 bool anychange;113 long *ixnew; /* will be overlaid on file[1] */114 long *ixold; /* will be overlaid on klist */115 struct cand *clist; /* merely a free storage pot for candidates */116 int clistlen; /* the length of clist */117 struct line *sfile[2]; /* shortened by pruning common prefix/suffix */118 struct context_vec *context_vec_start;119 struct context_vec *context_vec_end;120 struct context_vec *context_vec_ptr;121 struct stat stb1, stb2;122 };123 #define G (*ptr_to_globals)124 #define dl (G.dl )125 #define dl_count (G.dl_count )126 #define context (G.context )127 #define max_context (G.max_context )128 #define status (G.status )129 #define start (G.start )130 #define label1 (G.label1 )131 #define label2 (G.label2 )132 #define file (G.file )133 #define J (G.J )134 #define class (G.class )135 #define klist (G.klist )136 #define member (G.member )137 #define clen (G.clen )138 #define len (G.len )139 #define pref (G.pref )140 #define suff (G.suff )141 #define slen (G.slen )142 #define anychange (G.anychange )143 #define ixnew (G.ixnew )144 #define ixold (G.ixold )145 #define clist (G.clist )146 #define clistlen (G.clistlen )147 #define sfile (G.sfile )148 #define context_vec_start (G.context_vec_start )149 #define context_vec_end (G.context_vec_end )150 #define context_vec_ptr (G.context_vec_ptr )151 #define stb1 (G.stb1 )152 #define stb2 (G.stb2 )153 #define INIT_G() do { \154 PTR_TO_GLOBALS = xzalloc(sizeof(G)); \155 context = 3; \156 max_context = 64; \157 } while (0)158 159 160 static void print_only(const char *path, size_t dirlen, const char *entry)161 {162 if (dirlen > 1)163 dirlen--;164 printf("Only in %.*s: %s\n", (int) dirlen, path, entry);165 }166 167 static void print_status(int val, char *path1, char *path2, char *entry)168 {169 const char *const _entry = entry ? entry : "";170 char * const _path1 = entry ? concat_path_file(path1, _entry) : path1;171 char * const _path2 = entry ? concat_path_file(path2, _entry) : path2;172 173 switch (val) {174 case D_ONLY:175 print_only(path1, strlen(path1), entry);176 break;177 case D_COMMON:178 printf("Common subdirectories: %s and %s\n", _path1, _path2);179 break;180 case D_BINARY:181 printf("Binary files %s and %s differ\n", _path1, _path2);182 break;183 case D_DIFFER:184 if (option_mask32 & FLAG_q)185 printf("Files %s and %s differ\n", _path1, _path2);186 break;187 case D_SAME:188 if (option_mask32 & FLAG_s)189 printf("Files %s and %s are identical\n", _path1, _path2);190 break;191 case D_MISMATCH1:192 printf("File %s is a %s while file %s is a %s\n",193 _path1, "directory", _path2, "regular file");194 break;195 case D_MISMATCH2:196 printf("File %s is a %s while file %s is a %s\n",197 _path1, "regular file", _path2, "directory");198 break;199 case D_SKIPPED1:200 printf("File %s is not a regular file or directory and was skipped\n",201 _path1);202 break;203 case D_SKIPPED2:204 printf("File %s is not a regular file or directory and was skipped\n",205 _path2);206 break;207 }208 if (entry) {209 free(_path1);210 free(_path2);211 }212 }213 static ALWAYS_INLINE int fiddle_sum(int sum, int t)214 {215 return sum * 127 + t;216 }217 /*218 * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.219 */220 static int readhash(FILE *fp)221 {222 int i, t, space;223 int sum;224 225 sum = 1;226 space = 0;227 if (!(option_mask32 & (FLAG_b | FLAG_w))) {228 for (i = 0; (t = getc(fp)) != '\n'; i++) {229 if (t == EOF) {230 if (i == 0)231 return 0;232 break;233 }234 sum = fiddle_sum(sum, t);235 }236 } else {237 for (i = 0;;) {238 switch (t = getc(fp)) {239 case '\t':240 case '\r':241 case '\v':242 case '\f':243 case ' ':244 space++;245 continue;246 default:247 if (space && !(option_mask32 & FLAG_w)) {248 i++;249 space = 0;250 }251 sum = fiddle_sum(sum, t);252 i++;253 continue;254 case EOF:255 if (i == 0)256 return 0;257 /* FALLTHROUGH */258 case '\n':259 break;260 }261 break;262 }263 }264 /*265 * There is a remote possibility that we end up with a zero sum.266 * Zero is used as an EOF marker, so return 1 instead.267 */268 return (sum == 0 ? 1 : sum);269 }270 271 272 /*273 * Check to see if the given files differ.274 * Returns 0 if they are the same, 1 if different, and -1 on error.275 */276 static int files_differ(FILE *f1, FILE *f2, int flags)277 {278 size_t i, j;279 280 if ((flags & (D_EMPTY1 | D_EMPTY2)) || stb1.st_size != stb2.st_size281 || (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT)282 ) {283 return 1;284 }285 while (1) {286 i = fread(g_read_buf, 1, COMMON_BUFSIZE/2, f1);287 j = fread(g_read_buf + COMMON_BUFSIZE/2, 1, COMMON_BUFSIZE/2, f2);288 if (i != j)289 return 1;290 if (i == 0)291 return (ferror(f1) || ferror(f2));292 if (memcmp(g_read_buf,293 g_read_buf + COMMON_BUFSIZE/2, i) != 0)294 return 1;295 }296 }297 298 299 static void prepare(int i, FILE *fp /*, off_t filesize*/)300 {301 struct line *p;302 int h;303 size_t j, sz;304 305 rewind(fp);306 307 /*sz = (filesize <= FSIZE_MAX ? filesize : FSIZE_MAX) / 25;*/308 /*if (sz < 100)*/309 sz = 100;310 311 p = xmalloc((sz + 3) * sizeof(p[0]));312 j = 0;313 while ((h = readhash(fp))) {314 if (j == sz) {315 sz = sz * 3 / 2;316 p = xrealloc(p, (sz + 3) * sizeof(p[0]));317 }318 p[++j].value = h;319 }320 len[i] = j;321 file[i] = p;322 }323 324 325 static void prune(void)326 {327 int i, j;328 329 for (pref = 0; pref < len[0] && pref < len[1] &&330 file[0][pref + 1].value == file[1][pref + 1].value; pref++)331 continue;332 for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&333 file[0][len[0] - suff].value == file[1][len[1] - suff].value;334 suff++)335 continue;336 for (j = 0; j < 2; j++) {337 sfile[j] = file[j] + pref;338 slen[j] = len[j] - pref - suff;339 for (i = 0; i <= slen[j]; i++)340 sfile[j][i].serial = i;341 }342 }343 344 345 static void equiv(struct line *a, int n, struct line *b, int m, int *c)346 {347 int i, j;348 349 i = j = 1;350 while (i <= n && j <= m) {351 if (a[i].value < b[j].value)352 a[i++].value = 0;353 else if (a[i].value == b[j].value)354 a[i++].value = j;355 else356 j++;357 }358 while (i <= n)359 a[i++].value = 0;360 b[m + 1].value = 0;361 j = 0;362 while (++j <= m) {363 c[j] = -b[j].serial;364 while (b[j + 1].value == b[j].value) {365 j++;366 c[j] = b[j].serial;367 }368 }369 c[j] = -1;370 }371 372 373 static int isqrt(int n)374 {375 int y, x;376 377 if (n == 0)378 return 0;379 x = 1;380 do {381 y = x;382 x = n / x;383 x += y;384 x /= 2;385 } while ((x - y) > 1 || (x - y) < -1);386 387 return x;388 }389 390 391 static int newcand(int x, int y, int pred)392 {393 struct cand *q;394 395 if (clen == clistlen) {396 clistlen = clistlen * 11 / 10;397 clist = xrealloc(clist, clistlen * sizeof(struct cand));398 }399 q = clist + clen;400 q->x = x;401 q->y = y;402 q->pred = pred;403 return clen++;404 }405 406 407 static int search(int *c, int k, int y)408 {409 int i, j, l, t;410 411 if (clist[c[k]].y < y) /* quick look for typical case */412 return k + 1;413 i = 0;414 j = k + 1;415 while (1) {416 l = i + j;417 if ((l >>= 1) <= i)418 break;419 t = clist[c[l]].y;420 if (t > y)421 j = l;422 else if (t < y)423 i = l;424 else425 return l;426 }427 return l + 1;428 }429 430 431 static int stone(int *a, int n, int *b, int *c)432 {433 int i, k, y, j, l;434 int oldc, tc, oldl;435 unsigned int numtries;436 437 #if ENABLE_FEATURE_DIFF_MINIMAL438 const unsigned int bound =439 (option_mask32 & FLAG_d) ? UINT_MAX : MAX(256, isqrt(n));440 #else441 const unsigned int bound = MAX(256, isqrt(n));442 #endif443 k = 0;444 c[0] = newcand(0, 0, 0);445 for (i = 1; i <= n; i++) {446 j = a[i];447 if (j == 0)448 continue;449 y = -b[j];450 oldl = 0;451 oldc = c[0];452 numtries = 0;453 do {454 if (y <= clist[oldc].y)455 continue;456 l = search(c, k, y);457 if (l != oldl + 1)458 oldc = c[l - 1];459 if (l <= k) {460 if (clist[c[l]].y <= y)461 continue;462 tc = c[l];463 c[l] = newcand(i, y, oldc);464 oldc = tc;465 oldl = l;466 numtries++;467 } else {468 c[l] = newcand(i, y, oldc);469 k++;470 break;471 }472 } while ((y = b[++j]) > 0 && numtries < bound);473 }474 return k;475 }476 477 478 static void unravel(int p)479 {480 struct cand *q;481 int i;482 483 for (i = 0; i <= len[0]; i++)484 J[i] = i <= pref ? i : i > len[0] - suff ? i + len[1] - len[0] : 0;485 for (q = clist + p; q->y != 0; q = clist + q->pred)486 J[q->x + pref] = q->y + pref;487 }488 489 490 static void unsort(struct line *f, int l, int *b)491 {492 int *a, i;493 494 a = xmalloc((l + 1) * sizeof(int));495 for (i = 1; i <= l; i++)496 a[f[i].serial] = f[i].value;497 for (i = 1; i <= l; i++)498 b[i] = a[i];499 free(a);500 }501 502 503 static int skipline(FILE * f)504 {505 int i, c;506 507 for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)508 continue;509 return i;510 }511 512 513 /*514 * Check does double duty:515 * 1. ferret out any fortuitous correspondences due516 * to confounding by hashing (which result in "jackpot")517 * 2. collect random access indexes to the two files518 */519 static void check(FILE * f1, FILE * f2)520 {521 int i, j, jackpot, c, d;522 long ctold, ctnew;523 524 rewind(f1);525 rewind(f2);526 j = 1;527 ixold[0] = ixnew[0] = 0;528 jackpot = 0;529 ctold = ctnew = 0;530 for (i = 1; i <= len[0]; i++) {531 if (J[i] == 0) {532 ixold[i] = ctold += skipline(f1);533 continue;534 }535 while (j < J[i]) {536 ixnew[j] = ctnew += skipline(f2);537 j++;538 }539 if ((option_mask32 & FLAG_b) || (option_mask32 & FLAG_w)540 || (option_mask32 & FLAG_i)) {541 while (1) {542 c = getc(f1);543 d = getc(f2);544 /*545 * GNU diff ignores a missing newline546 * in one file if bflag || wflag.547 */548 if (((option_mask32 & FLAG_b) || (option_mask32 & FLAG_w)) &&549 ((c == EOF && d == '\n') || (c == '\n' && d == EOF))) {550 break;551 }552 ctold++;553 ctnew++;554 if ((option_mask32 & FLAG_b) && isspace(c) && isspace(d)) {555 do {556 if (c == '\n')557 break;558 ctold++;559 } while (isspace(c = getc(f1)));560 do {561 if (d == '\n')562 break;563 ctnew++;564 } while (isspace(d = getc(f2)));565 } else if (option_mask32 & FLAG_w) {566 while (isspace(c) && c != '\n') {567 c = getc(f1);568 ctold++;569 }570 while (isspace(d) && d != '\n') {571 d = getc(f2);572 ctnew++;573 }574 }575 if (c != d) {576 jackpot++;577 J[i] = 0;578 if (c != '\n' && c != EOF)579 ctold += skipline(f1);580 if (d != '\n' && c != EOF)581 ctnew += skipline(f2);582 break;583 }584 if (c == '\n' || c == EOF)585 break;586 }587 } else {588 while (1) {589 ctold++;590 ctnew++;591 if ((c = getc(f1)) != (d = getc(f2))) {592 J[i] = 0;593 if (c != '\n' && c != EOF)594 ctold += skipline(f1);595 if (d != '\n' && c != EOF)596 ctnew += skipline(f2);597 break;598 }599 if (c == '\n' || c == EOF)600 break;601 }602 }603 ixold[i] = ctold;604 ixnew[j] = ctnew;605 j++;606 }607 for (; j <= len[1]; j++)608 ixnew[j] = ctnew += skipline(f2);609 }610 611 612 /* shellsort CACM #201 */613 static void sort(struct line *a, int n)614 {615 struct line *ai, *aim, w;616 int j, m = 0, k;617 618 if (n == 0)619 return;620 for (j = 1; j <= n; j *= 2)621 m = 2 * j - 1;622 for (m /= 2; m != 0; m /= 2) {623 k = n - m;624 for (j = 1; j <= k; j++) {625 for (ai = &a[j]; ai > a; ai -= m) {626 aim = &ai[m];627 if (aim < ai)628 break; /* wraparound */629 if (aim->value > ai[0].value ||630 (aim->value == ai[0].value && aim->serial > ai[0].serial))631 break;632 w.value = ai[0].value;633 ai[0].value = aim->value;634 aim->value = w.value;635 w.serial = ai[0].serial;636 ai[0].serial = aim->serial;637 aim->serial = w.serial;638 }639 }640 }641 }642 643 644 static void uni_range(int a, int b)645 {646 if (a < b)647 printf("%d,%d", a, b - a + 1);648 else if (a == b)649 printf("%d", b);650 else651 printf("%d,0", b);652 }653 654 655 static void fetch(long *f, int a, int b, FILE * lb, int ch)656 {657 int i, j, c, lastc, col, nc;658 659 if (a > b)660 return;661 for (i = a; i <= b; i++) {662 fseek(lb, f[i - 1], SEEK_SET);663 nc = f[i] - f[i - 1];664 if (ch != '\0') {665 putchar(ch);666 if (option_mask32 & FLAG_T)667 putchar('\t');668 }669 col = 0;670 for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {671 if ((c = getc(lb)) == EOF) {672 printf("\n\\ No newline at end of file\n");673 return;674 }675 if (c == '\t' && (option_mask32 & FLAG_t)) {676 do {677 putchar(' ');678 } while (++col & 7);679 } else {680 putchar(c);681 col++;682 }683 }684 }685 }686 687 688 static int asciifile(FILE * f)689 {690 #if ENABLE_FEATURE_DIFF_BINARY691 int i, cnt;692 #endif693 694 if ((option_mask32 & FLAG_a) || f == NULL)695 return 1;696 697 #if ENABLE_FEATURE_DIFF_BINARY698 rewind(f);699 cnt = fread(g_read_buf, 1, COMMON_BUFSIZE, f);700 for (i = 0; i < cnt; i++) {701 if (!isprint(g_read_buf[i])702 && !isspace(g_read_buf[i])) {703 return 0;704 }705 }706 #endif707 return 1;708 }709 710 711 /* dump accumulated "unified" diff changes */712 static void dump_unified_vec(FILE * f1, FILE * f2)713 {714 struct context_vec *cvp = context_vec_start;715 int lowa, upb, lowc, upd;716 int a, b, c, d;717 char ch;718 719 if (context_vec_start > context_vec_ptr)720 return;721 722 b = d = 0; /* gcc */723 lowa = MAX(1, cvp->a - context);724 upb = MIN(len[0], context_vec_ptr->b + context);725 lowc = MAX(1, cvp->c - context);726 upd = MIN(len[1], context_vec_ptr->d + context);727 728 printf("@@ -");729 uni_range(lowa, upb);730 printf(" +");731 uni_range(lowc, upd);732 printf(" @@\n");733 734 /*735 * Output changes in "unified" diff format--the old and new lines736 * are printed together.737 */738 for (; cvp <= context_vec_ptr; cvp++) {739 a = cvp->a;740 b = cvp->b;741 c = cvp->c;742 d = cvp->d;743 744 /*745 * c: both new and old changes746 * d: only changes in the old file747 * a: only changes in the new file748 */749 if (a <= b && c <= d)750 ch = 'c';751 else752 ch = (a <= b) ? 'd' : 'a';753 #if 0754 switch (ch) {755 case 'c':756 fetch(ixold, lowa, a - 1, f1, ' ');757 fetch(ixold, a, b, f1, '-');758 fetch(ixnew, c, d, f2, '+');759 break;760 case 'd':761 fetch(ixold, lowa, a - 1, f1, ' ');762 fetch(ixold, a, b, f1, '-');763 break;764 case 'a':765 fetch(ixnew, lowc, c - 1, f2, ' ');766 fetch(ixnew, c, d, f2, '+');767 break;768 }769 #else770 if (ch == 'c' || ch == 'd') {771 fetch(ixold, lowa, a - 1, f1, ' ');772 fetch(ixold, a, b, f1, '-');773 }774 if (ch == 'a')775 fetch(ixnew, lowc, c - 1, f2, ' ');776 if (ch == 'c' || ch == 'a')777 fetch(ixnew, c, d, f2, '+');778 #endif779 lowa = b + 1;780 lowc = d + 1;781 }782 fetch(ixnew, d + 1, upd, f2, ' ');783 784 context_vec_ptr = context_vec_start - 1;785 }786 787 788 static void print_header(const char *file1, const char *file2)789 {790 if (label1)791 printf("--- %s\n", label1);792 else793 printf("--- %s\t%s", file1, ctime(&stb1.st_mtime));794 if (label2)795 printf("+++ %s\n", label2);796 else797 printf("+++ %s\t%s", file2, ctime(&stb2.st_mtime));798 }799 800 801 /*802 * Indicate that there is a difference between lines a and b of the from file803 * to get to lines c to d of the to file. If a is greater than b then there804 * are no lines in the from file involved and this means that there were805 * lines appended (beginning at b). If c is greater than d then there are806 * lines missing from the to file.807 */808 static void change(char *file1, FILE * f1, char *file2, FILE * f2, int a,809 int b, int c, int d)810 {811 if ((a > b && c > d) || (option_mask32 & FLAG_q)) {812 anychange = 1;813 return;814 }815 816 /*817 * Allocate change records as needed.818 */819 if (context_vec_ptr == context_vec_end - 1) {820 ptrdiff_t offset = context_vec_ptr - context_vec_start;821 822 max_context <<= 1;823 context_vec_start = xrealloc(context_vec_start,824 max_context * sizeof(struct context_vec));825 context_vec_end = context_vec_start + max_context;826 context_vec_ptr = context_vec_start + offset;827 }828 if (anychange == 0) {829 /*830 * Print the context/unidiff header first time through.831 */832 print_header(file1, file2);833 } else if (a > context_vec_ptr->b + (2 * context) + 1 &&834 c > context_vec_ptr->d + (2 * context) + 1) {835 /*836 * If this change is more than 'context' lines from the837 * previous change, dump the record and reset it.838 */839 dump_unified_vec(f1, f2);840 }841 context_vec_ptr++;842 context_vec_ptr->a = a;843 context_vec_ptr->b = b;844 context_vec_ptr->c = c;845 context_vec_ptr->d = d;846 anychange = 1;847 }848 849 850 static void output(char *file1, FILE * f1, char *file2, FILE * f2)851 {852 /* Note that j0 and j1 can't be used as they are defined in math.h.853 * This also allows the rather amusing variable 'j00'... */854 int m, i0, i1, j00, j01;855 856 rewind(f1);857 rewind(f2);858 m = len[0];859 J[0] = 0;860 J[m + 1] = len[1] + 1;861 for (i0 = 1; i0 <= m; i0 = i1 + 1) {862 while (i0 <= m && J[i0] == J[i0 - 1] + 1)863 i0++;864 j00 = J[i0 - 1] + 1;865 i1 = i0 - 1;866 while (i1 < m && J[i1 + 1] == 0)867 i1++;868 j01 = J[i1 + 1] - 1;869 J[i1] = j01;870 change(file1, f1, file2, f2, i0, i1, j00, j01);871 }872 if (m == 0) {873 change(file1, f1, file2, f2, 1, 0, 1, len[1]);874 }875 if (anychange != 0 && !(option_mask32 & FLAG_q)) {876 dump_unified_vec(f1, f2);877 }878 }879 15 880 16 /* … … 885 21 * The major goal is to generate the match vector J. 886 22 * J[i] is the index of the line in file1 corresponding 887 * to line i file0. J[i] = 0 if there is no23 * to line i in file0. J[i] = 0 if there is no 888 24 * such line in file1. 889 25 * 890 26 * Lines are hashed so as to work in core. All potential 891 27 * matches are located by sorting the lines of each file 892 * on the hash (called ``value''). In particular, this28 * on the hash (called "value"). In particular, this 893 29 * collects the equivalence classes in file1 together. 894 30 * Subroutine equiv replaces the value of each line in … … 906 42 * through the lines of file0, developing a vector klist 907 43 * of "k-candidates". At step i a k-candidate is a matched 908 * pair of lines x,y (x in file0 y in file1) such that44 * pair of lines x,y (x in file0, y in file1) such that 909 45 * there is a common subsequence of length k 910 46 * between the first i lines of file0 and the first y … … 937 73 * The core requirements for problems larger than somewhat 938 74 * are (in words) 2*length(file0) + length(file1) + 939 * 3*(number of k-candidates installed), 75 * 3*(number of k-candidates installed), typically about 940 76 * 6n words for files of length n. 941 77 */ 942 static unsigned diffreg(char *ofile1, char *ofile2, int flags) 943 { 944 char *file1 = ofile1; 945 char *file2 = ofile2; 946 FILE *f1 = stdin, *f2 = stdin; 947 unsigned rval; 78 79 #include "libbb.h" 80 81 #if 0 82 //#define dbg_error_msg(...) bb_error_msg(__VA_ARGS__) 83 #else 84 #define dbg_error_msg(...) ((void)0) 85 #endif 86 87 enum { /* print_status() and diffreg() return values */ 88 STATUS_SAME, /* files are the same */ 89 STATUS_DIFFER, /* files differ */ 90 STATUS_BINARY, /* binary files differ */ 91 }; 92 93 enum { /* Commandline flags */ 94 FLAG_a, 95 FLAG_b, 96 FLAG_d, 97 FLAG_i, 98 FLAG_L, /* never used, handled by getopt32 */ 99 FLAG_N, 100 FLAG_q, 101 FLAG_r, 102 FLAG_s, 103 FLAG_S, /* never used, handled by getopt32 */ 104 FLAG_t, 105 FLAG_T, 106 FLAG_U, /* never used, handled by getopt32 */ 107 FLAG_w, 108 FLAG_u, /* ignored, this is the default */ 109 FLAG_p, /* not implemented */ 110 FLAG_B, 111 FLAG_E, /* not implemented */ 112 }; 113 #define FLAG(x) (1 << FLAG_##x) 114 115 /* We cache file position to avoid excessive seeking */ 116 typedef struct FILE_and_pos_t { 117 FILE *ft_fp; 118 off_t ft_pos; 119 } FILE_and_pos_t; 120 121 struct globals { 122 smallint exit_status; 123 int opt_U_context; 124 const char *other_dir; 125 char *label[2]; 126 struct stat stb[2]; 127 }; 128 #define G (*ptr_to_globals) 129 #define exit_status (G.exit_status ) 130 #define opt_U_context (G.opt_U_context ) 131 #define label (G.label ) 132 #define stb (G.stb ) 133 #define INIT_G() do { \ 134 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 135 opt_U_context = 3; \ 136 } while (0) 137 138 typedef int token_t; 139 140 enum { 141 /* Public */ 142 TOK_EMPTY = 1 << 9, /* Line fully processed, you can proceed to the next */ 143 TOK_EOF = 1 << 10, /* File ended */ 144 /* Private (Only to be used by read_token() */ 145 TOK_EOL = 1 << 11, /* we saw EOL (sticky) */ 146 TOK_SPACE = 1 << 12, /* used -b code, means we are skipping spaces */ 147 SHIFT_EOF = (sizeof(token_t)*8 - 8) - 1, 148 CHAR_MASK = 0x1ff, /* 8th bit is used to distinguish EOF from 0xff */ 149 }; 150 151 /* Restores full EOF from one 8th bit: */ 152 //#define TOK2CHAR(t) (((t) << SHIFT_EOF) >> SHIFT_EOF) 153 /* We don't really need the above, we only need to have EOF != any_real_char: */ 154 #define TOK2CHAR(t) ((t) & CHAR_MASK) 155 156 static void seek_ft(FILE_and_pos_t *ft, off_t pos) 157 { 158 if (ft->ft_pos != pos) { 159 ft->ft_pos = pos; 160 fseeko(ft->ft_fp, pos, SEEK_SET); 161 } 162 } 163 164 /* Reads tokens from given fp, handling -b and -w flags 165 * The user must reset tok every line start 166 */ 167 static int read_token(FILE_and_pos_t *ft, token_t tok) 168 { 169 tok |= TOK_EMPTY; 170 while (!(tok & TOK_EOL)) { 171 bool is_space; 172 int t; 173 174 t = fgetc(ft->ft_fp); 175 if (t != EOF) 176 ft->ft_pos++; 177 is_space = (t == EOF || isspace(t)); 178 179 /* If t == EOF (-1), set both TOK_EOF and TOK_EOL */ 180 tok |= (t & (TOK_EOF + TOK_EOL)); 181 /* Only EOL? */ 182 if (t == '\n') 183 tok |= TOK_EOL; 184 185 if (option_mask32 & FLAG(i)) /* Handcoded tolower() */ 186 t = (t >= 'A' && t <= 'Z') ? t - ('A' - 'a') : t; 187 188 if ((option_mask32 & FLAG(w)) && is_space) 189 continue; 190 191 /* Trim char value to low 9 bits */ 192 t &= CHAR_MASK; 193 194 if (option_mask32 & FLAG(b)) { 195 /* Was prev char whitespace? */ 196 if (tok & TOK_SPACE) { /* yes */ 197 if (is_space) /* this one too, ignore it */ 198 continue; 199 tok &= ~TOK_SPACE; 200 } else if (is_space) { 201 /* 1st whitespace char. 202 * Set TOK_SPACE and replace char by ' ' */ 203 t = TOK_SPACE + ' '; 204 } 205 } 206 /* Clear EMPTY */ 207 tok &= ~(TOK_EMPTY + CHAR_MASK); 208 /* Assign char value (low 9 bits) and maybe set TOK_SPACE */ 209 tok |= t; 210 break; 211 } 212 #if 0 213 bb_error_msg("fp:%p tok:%x '%c'%s%s%s%s", fp, tok, tok & 0xff 214 , tok & TOK_EOF ? " EOF" : "" 215 , tok & TOK_EOL ? " EOL" : "" 216 , tok & TOK_EMPTY ? " EMPTY" : "" 217 , tok & TOK_SPACE ? " SPACE" : "" 218 ); 219 #endif 220 return tok; 221 } 222 223 struct cand { 224 int x; 225 int y; 226 int pred; 227 }; 228 229 static int search(const int *c, int k, int y, const struct cand *list) 230 { 231 int i, j; 232 233 if (list[c[k]].y < y) /* quick look for typical case */ 234 return k + 1; 235 236 for (i = 0, j = k + 1;;) { 237 const int l = (i + j) >> 1; 238 if (l > i) { 239 const int t = list[c[l]].y; 240 if (t > y) 241 j = l; 242 else if (t < y) 243 i = l; 244 else 245 return l; 246 } else 247 return l + 1; 248 } 249 } 250 251 static unsigned isqrt(unsigned n) 252 { 253 unsigned x = 1; 254 while (1) { 255 const unsigned y = x; 256 x = ((n / x) + x) >> 1; 257 if (x <= (y + 1) && x >= (y - 1)) 258 return x; 259 } 260 } 261 262 static void stone(const int *a, int n, const int *b, int *J, int pref) 263 { 264 const unsigned isq = isqrt(n); 265 const unsigned bound = 266 (option_mask32 & FLAG(d)) ? UINT_MAX : MAX(256, isq); 267 int clen = 1; 268 int clistlen = 100; 269 int k = 0; 270 struct cand *clist = xzalloc(clistlen * sizeof(clist[0])); 271 struct cand cand; 272 struct cand *q; 273 int *klist = xzalloc((n + 2) * sizeof(klist[0])); 274 /*clist[0] = (struct cand){0}; - xzalloc did it */ 275 /*klist[0] = 0; */ 276 277 for (cand.x = 1; cand.x <= n; cand.x++) { 278 int j = a[cand.x], oldl = 0; 279 unsigned numtries = 0; 280 if (j == 0) 281 continue; 282 cand.y = -b[j]; 283 cand.pred = klist[0]; 284 do { 285 int l, tc; 286 if (cand.y <= clist[cand.pred].y) 287 continue; 288 l = search(klist, k, cand.y, clist); 289 if (l != oldl + 1) 290 cand.pred = klist[l - 1]; 291 if (l <= k && clist[klist[l]].y <= cand.y) 292 continue; 293 if (clen == clistlen) { 294 clistlen = clistlen * 11 / 10; 295 clist = xrealloc(clist, clistlen * sizeof(clist[0])); 296 } 297 clist[clen] = cand; 298 tc = klist[l]; 299 klist[l] = clen++; 300 if (l <= k) { 301 cand.pred = tc; 302 oldl = l; 303 numtries++; 304 } else { 305 k++; 306 break; 307 } 308 } while ((cand.y = b[++j]) > 0 && numtries < bound); 309 } 310 /* Unravel */ 311 for (q = clist + klist[k]; q->y; q = clist + q->pred) 312 J[q->x + pref] = q->y + pref; 313 free(klist); 314 free(clist); 315 } 316 317 struct line { 318 /* 'serial' is not used in the begining, so we reuse it 319 * to store line offsets, thus reducing memory pressure 320 */ 321 union { 322 unsigned serial; 323 off_t offset; 324 }; 325 unsigned value; 326 }; 327 328 static void equiv(struct line *a, int n, struct line *b, int m, int *c) 329 { 330 int i = 1, j = 1; 331 332 while (i <= n && j <= m) { 333 if (a[i].value < b[j].value) 334 a[i++].value = 0; 335 else if (a[i].value == b[j].value) 336 a[i++].value = j; 337 else 338 j++; 339 } 340 while (i <= n) 341 a[i++].value = 0; 342 b[m + 1].value = 0; 343 j = 0; 344 while (++j <= m) { 345 c[j] = -b[j].serial; 346 while (b[j + 1].value == b[j].value) { 347 j++; 348 c[j] = b[j].serial; 349 } 350 } 351 c[j] = -1; 352 } 353 354 static void unsort(const struct line *f, int l, int *b) 355 { 948 356 int i; 949 950 anychange = 0; 951 context_vec_ptr = context_vec_start - 1; 952 953 if (S_ISDIR(stb1.st_mode) != S_ISDIR(stb2.st_mode)) 954 return (S_ISDIR(stb1.st_mode) ? D_MISMATCH1 : D_MISMATCH2); 955 956 rval = D_SAME; 957 958 if (LONE_DASH(file1) && LONE_DASH(file2)) 959 goto closem; 960 961 if (flags & D_EMPTY1) 962 f1 = xfopen(bb_dev_null, "r"); 963 else if (NOT_LONE_DASH(file1)) 964 f1 = xfopen(file1, "r"); 965 if (flags & D_EMPTY2) 966 f2 = xfopen(bb_dev_null, "r"); 967 else if (NOT_LONE_DASH(file2)) 968 f2 = xfopen(file2, "r"); 969 970 /* We can't diff non-seekable stream - we use rewind(), fseek(). 971 * This can be fixed (volunteers?). 972 * Meanwhile we should check it here by stat'ing input fds, 973 * but I am lazy and check that in main() instead. 974 * Check in main won't catch "diffing fifos buried in subdirectories" 975 * failure scenario - not very likely in real life... */ 976 977 i = files_differ(f1, f2, flags); 978 if (i == 0) 979 goto closem; 980 else if (i != 1) { /* 1 == ok */ 981 /* error */ 982 status |= 2; 983 goto closem; 984 } 985 986 if (!asciifile(f1) || !asciifile(f2)) { 987 rval = D_BINARY; 988 status |= 1; 989 goto closem; 990 } 991 992 prepare(0, f1 /*, stb1.st_size*/); 993 prepare(1, f2 /*, stb2.st_size*/); 994 prune(); 995 sort(sfile[0], slen[0]); 996 sort(sfile[1], slen[1]); 997 998 member = (int *) file[1]; 357 int *a = xmalloc((l + 1) * sizeof(a[0])); 358 for (i = 1; i <= l; i++) 359 a[f[i].serial] = f[i].value; 360 for (i = 1; i <= l; i++) 361 b[i] = a[i]; 362 free(a); 363 } 364 365 static int line_compar(const void *a, const void *b) 366 { 367 #define l0 ((const struct line*)a) 368 #define l1 ((const struct line*)b) 369 int r = l0->value - l1->value; 370 if (r) 371 return r; 372 return l0->serial - l1->serial; 373 #undef l0 374 #undef l1 375 } 376 377 static void fetch(FILE_and_pos_t *ft, const off_t *ix, int a, int b, int ch) 378 { 379 int i, j, col; 380 for (i = a; i <= b; i++) { 381 seek_ft(ft, ix[i - 1]); 382 putchar(ch); 383 if (option_mask32 & FLAG(T)) 384 putchar('\t'); 385 for (j = 0, col = 0; j < ix[i] - ix[i - 1]; j++) { 386 int c = fgetc(ft->ft_fp); 387 if (c == EOF) { 388 printf("\n\\ No newline at end of file\n"); 389 return; 390 } 391 ft->ft_pos++; 392 if (c == '\t' && (option_mask32 & FLAG(t))) 393 do putchar(' '); while (++col & 7); 394 else { 395 putchar(c); 396 col++; 397 } 398 } 399 } 400 } 401 402 /* Creates the match vector J, where J[i] is the index 403 * of the line in the new file corresponding to the line i 404 * in the old file. Lines start at 1 instead of 0, that value 405 * being used instead to denote no corresponding line. 406 * This vector is dynamically allocated and must be freed by the caller. 407 * 408 * * fp is an input parameter, where fp[0] and fp[1] are the open 409 * old file and new file respectively. 410 * * nlen is an output variable, where nlen[0] and nlen[1] 411 * gets the number of lines in the old and new file respectively. 412 * * ix is an output variable, where ix[0] and ix[1] gets 413 * assigned dynamically allocated vectors of the offsets of the lines 414 * of the old and new file respectively. These must be freed by the caller. 415 */ 416 static NOINLINE int *create_J(FILE_and_pos_t ft[2], int nlen[2], off_t *ix[2]) 417 { 418 int *J, slen[2], *class, *member; 419 struct line *nfile[2], *sfile[2]; 420 int pref = 0, suff = 0, i, j, delta; 421 422 /* Lines of both files are hashed, and in the process 423 * their offsets are stored in the array ix[fileno] 424 * where fileno == 0 points to the old file, and 425 * fileno == 1 points to the new one. 426 */ 427 for (i = 0; i < 2; i++) { 428 unsigned hash; 429 token_t tok; 430 size_t sz = 100; 431 nfile[i] = xmalloc((sz + 3) * sizeof(nfile[i][0])); 432 /* ft gets here without the correct position, cant use seek_ft */ 433 ft[i].ft_pos = 0; 434 fseeko(ft[i].ft_fp, 0, SEEK_SET); 435 436 nlen[i] = 0; 437 /* We could zalloc nfile, but then zalloc starts showing in gprof at ~1% */ 438 nfile[i][0].offset = 0; 439 goto start; /* saves code */ 440 while (1) { 441 tok = read_token(&ft[i], tok); 442 if (!(tok & TOK_EMPTY)) { 443 /* Hash algorithm taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578. */ 444 /*hash = hash * 128 - hash + TOK2CHAR(tok); 445 * gcc insists on optimizing above to "hash * 127 + ...", thus... */ 446 unsigned o = hash - TOK2CHAR(tok); 447 hash = hash * 128 - o; /* we want SPEED here */ 448 continue; 449 } 450 if (nlen[i]++ == sz) { 451 sz = sz * 3 / 2; 452 nfile[i] = xrealloc(nfile[i], (sz + 3) * sizeof(nfile[i][0])); 453 } 454 /* line_compar needs hashes fit into positive int */ 455 nfile[i][nlen[i]].value = hash & INT_MAX; 456 /* like ftello(ft[i].ft_fp) but faster (avoids lseek syscall) */ 457 nfile[i][nlen[i]].offset = ft[i].ft_pos; 458 if (tok & TOK_EOF) { 459 /* EOF counts as a token, so we have to adjust it here */ 460 nfile[i][nlen[i]].offset++; 461 break; 462 } 463 start: 464 hash = tok = 0; 465 } 466 /* Exclude lone EOF line from the end of the file, to make fetch()'s job easier */ 467 if (nfile[i][nlen[i]].offset - nfile[i][nlen[i] - 1].offset == 1) 468 nlen[i]--; 469 /* Now we copy the line offsets into ix */ 470 ix[i] = xmalloc((nlen[i] + 2) * sizeof(ix[i][0])); 471 for (j = 0; j < nlen[i] + 1; j++) 472 ix[i][j] = nfile[i][j].offset; 473 } 474 475 /* length of prefix and suffix is calculated */ 476 for (; pref < nlen[0] && pref < nlen[1] && 477 nfile[0][pref + 1].value == nfile[1][pref + 1].value; 478 pref++); 479 for (; suff < nlen[0] - pref && suff < nlen[1] - pref && 480 nfile[0][nlen[0] - suff].value == nfile[1][nlen[1] - suff].value; 481 suff++); 482 /* Arrays are pruned by the suffix and prefix length, 483 * the result being sorted and stored in sfile[fileno], 484 * and their sizes are stored in slen[fileno] 485 */ 486 for (j = 0; j < 2; j++) { 487 sfile[j] = nfile[j] + pref; 488 slen[j] = nlen[j] - pref - suff; 489 for (i = 0; i <= slen[j]; i++) 490 sfile[j][i].serial = i; 491 qsort(sfile[j] + 1, slen[j], sizeof(*sfile[j]), line_compar); 492 } 493 /* nfile arrays are reused to reduce memory pressure 494 * The #if zeroed out section performs the same task as the 495 * one in the #else section. 496 * Peak memory usage is higher, but one array copy is avoided 497 * by not using unsort() 498 */ 499 #if 0 500 member = xmalloc((slen[1] + 2) * sizeof(member[0])); 999 501 equiv(sfile[0], slen[0], sfile[1], slen[1], member); 1000 member = xrealloc(member, (slen[1] + 2) * sizeof(int)); 1001 1002 class = (int *) file[0]; 1003 unsort(sfile[0], slen[0], class); 1004 class = xrealloc(class, (slen[0] + 2) * sizeof(int)); 1005 1006 klist = xmalloc((slen[0] + 2) * sizeof(int)); 1007 clen = 0; 1008 clistlen = 100; 1009 clist = xmalloc(clistlen * sizeof(struct cand)); 1010 i = stone(class, slen[0], member, klist); 502 free(nfile[1]); 503 504 class = xmalloc((slen[0] + 1) * sizeof(class[0])); 505 for (i = 1; i <= slen[0]; i++) /* Unsorting */ 506 class[sfile[0][i].serial] = sfile[0][i].value; 507 free(nfile[0]); 508 #else 509 member = (int *)nfile[1]; 510 equiv(sfile[0], slen[0], sfile[1], slen[1], member); 511 member = xrealloc(member, (slen[1] + 2) * sizeof(member[0])); 512 513 class = (int *)nfile[0]; 514 unsort(sfile[0], slen[0], (int *)nfile[0]); 515 class = xrealloc(class, (slen[0] + 2) * sizeof(class[0])); 516 #endif 517 J = xmalloc((nlen[0] + 2) * sizeof(J[0])); 518 /* The elements of J which fall inside the prefix and suffix regions 519 * are marked as unchanged, while the ones which fall outside 520 * are initialized with 0 (no matches), so that function stone can 521 * then assign them their right values 522 */ 523 for (i = 0, delta = nlen[1] - nlen[0]; i <= nlen[0]; i++) 524 J[i] = i <= pref ? i : 525 i > (nlen[0] - suff) ? (i + delta) : 0; 526 /* Here the magic is performed */ 527 stone(class, slen[0], member, J, pref); 528 J[nlen[0] + 1] = nlen[1] + 1; 529 530 free(class); 1011 531 free(member); 1012 free(class); 1013 1014 J = xrealloc(J, (len[0] + 2) * sizeof(int)); 1015 unravel(klist[i]); 1016 free(clist); 1017 free(klist); 1018 1019 ixold = xrealloc(ixold, (len[0] + 2) * sizeof(long)); 1020 ixnew = xrealloc(ixnew, (len[1] + 2) * sizeof(long)); 1021 check(f1, f2); 1022 output(file1, f1, file2, f2); 1023 1024 closem: 1025 if (anychange) { 1026 status |= 1; 1027 if (rval == D_SAME) 1028 rval = D_DIFFER; 1029 } 1030 fclose_if_not_stdin(f1); 1031 fclose_if_not_stdin(f2); 1032 if (file1 != ofile1) 1033 free(file1); 1034 if (file2 != ofile2) 1035 free(file2); 1036 return rval; 1037 } 1038 532 533 /* Both files are rescanned, in an effort to find any lines 534 * which, due to limitations intrinsic to any hashing algorithm, 535 * are different but ended up confounded as the same 536 */ 537 for (i = 1; i <= nlen[0]; i++) { 538 if (!J[i]) 539 continue; 540 541 seek_ft(&ft[0], ix[0][i - 1]); 542 seek_ft(&ft[1], ix[1][J[i] - 1]); 543 544 for (j = J[i]; i <= nlen[0] && J[i] == j; i++, j++) { 545 token_t tok0 = 0, tok1 = 0; 546 do { 547 tok0 = read_token(&ft[0], tok0); 548 tok1 = read_token(&ft[1], tok1); 549 550 if (((tok0 ^ tok1) & TOK_EMPTY) != 0 /* one is empty (not both) */ 551 || (!(tok0 & TOK_EMPTY) && TOK2CHAR(tok0) != TOK2CHAR(tok1)) 552 ) { 553 J[i] = 0; /* Break the correspondence */ 554 } 555 } while (!(tok0 & tok1 & TOK_EMPTY)); 556 } 557 } 558 559 return J; 560 } 561 562 static bool diff(FILE* fp[2], char *file[2]) 563 { 564 int nlen[2]; 565 off_t *ix[2]; 566 FILE_and_pos_t ft[2]; 567 typedef struct { int a, b; } vec_t[2]; 568 vec_t *vec = NULL; 569 int i = 1, j, k, idx = -1; 570 bool anychange = false; 571 int *J; 572 573 ft[0].ft_fp = fp[0]; 574 ft[1].ft_fp = fp[1]; 575 /* note that ft[i].ft_pos is unintitalized, create_J() 576 * must not assume otherwise */ 577 J = create_J(ft, nlen, ix); 578 579 do { 580 bool nonempty = false; 581 582 while (1) { 583 vec_t v; 584 585 for (v[0].a = i; v[0].a <= nlen[0] && J[v[0].a] == J[v[0].a - 1] + 1; v[0].a++) 586 continue; 587 v[1].a = J[v[0].a - 1] + 1; 588 589 for (v[0].b = v[0].a - 1; v[0].b < nlen[0] && !J[v[0].b + 1]; v[0].b++) 590 continue; 591 v[1].b = J[v[0].b + 1] - 1; 592 /* 593 * Indicate that there is a difference between lines a and b of the 'from' file 594 * to get to lines c to d of the 'to' file. If a is greater than b then there 595 * are no lines in the 'from' file involved and this means that there were 596 * lines appended (beginning at b). If c is greater than d then there are 597 * lines missing from the 'to' file. 598 */ 599 if (v[0].a <= v[0].b || v[1].a <= v[1].b) { 600 /* 601 * If this change is more than 'context' lines from the 602 * previous change, dump the record and reset it. 603 */ 604 int ct = (2 * opt_U_context) + 1; 605 if (idx >= 0 606 && v[0].a > vec[idx][0].b + ct 607 && v[1].a > vec[idx][1].b + ct 608 ) { 609 break; 610 } 611 612 for (j = 0; j < 2; j++) 613 for (k = v[j].a; k < v[j].b; k++) 614 nonempty |= (ix[j][k+1] - ix[j][k] != 1); 615 616 vec = xrealloc_vector(vec, 6, ++idx); 617 memcpy(vec[idx], v, sizeof(v)); 618 } 619 620 i = v[0].b + 1; 621 if (i > nlen[0]) 622 break; 623 J[v[0].b] = v[1].b; 624 } 625 if (idx < 0 || ((option_mask32 & FLAG(B)) && !nonempty)) 626 goto cont; 627 if (!(option_mask32 & FLAG(q))) { 628 int lowa; 629 vec_t span, *cvp = vec; 630 631 if (!anychange) { 632 /* Print the context/unidiff header first time through */ 633 printf("--- %s\n", label[0] ? label[0] : file[0]); 634 printf("+++ %s\n", label[1] ? label[1] : file[1]); 635 } 636 637 printf("@@"); 638 for (j = 0; j < 2; j++) { 639 int a = span[j].a = MAX(1, (*cvp)[j].a - opt_U_context); 640 int b = span[j].b = MIN(nlen[j], vec[idx][j].b + opt_U_context); 641 642 printf(" %c%d", j ? '+' : '-', MIN(a, b)); 643 if (a == b) 644 continue; 645 printf(",%d", (a < b) ? b - a + 1 : 0); 646 } 647 printf(" @@\n"); 648 /* 649 * Output changes in "unified" diff format--the old and new lines 650 * are printed together. 651 */ 652 for (lowa = span[0].a; ; lowa = (*cvp++)[0].b + 1) { 653 bool end = cvp > &vec[idx]; 654 fetch(&ft[0], ix[0], lowa, end ? span[0].b : (*cvp)[0].a - 1, ' '); 655 if (end) 656 break; 657 for (j = 0; j < 2; j++) 658 fetch(&ft[j], ix[j], (*cvp)[j].a, (*cvp)[j].b, j ? '+' : '-'); 659 } 660 } 661 anychange = true; 662 cont: 663 idx = -1; 664 } while (i <= nlen[0]); 665 666 free(vec); 667 free(ix[0]); 668 free(ix[1]); 669 free(J); 670 return anychange; 671 } 672 673 static int diffreg(char *file[2]) 674 { 675 FILE *fp[2] = { stdin, stdin }; 676 bool binary = false, differ = false; 677 int status = STATUS_SAME, i; 678 679 for (i = 0; i < 2; i++) { 680 int fd = open_or_warn_stdin(file[i]); 681 if (fd == -1) 682 goto out; 683 /* Our diff implementation is using seek. 684 * When we meet non-seekable file, we must make a temp copy. 685 */ 686 if (lseek(fd, 0, SEEK_SET) == -1 && errno == ESPIPE) { 687 char name[] = "/tmp/difXXXXXX"; 688 int fd_tmp = xmkstemp(name); 689 690 unlink(name); 691 if (bb_copyfd_eof(fd, fd_tmp) < 0) 692 xfunc_die(); 693 if (fd) /* Prevents closing of stdin */ 694 close(fd); 695 fd = fd_tmp; 696 } 697 fp[i] = fdopen(fd, "r"); 698 } 699 700 while (1) { 701 const size_t sz = COMMON_BUFSIZE / 2; 702 char *const buf0 = bb_common_bufsiz1; 703 char *const buf1 = buf0 + sz; 704 int j, k; 705 i = fread(buf0, 1, sz, fp[0]); 706 j = fread(buf1, 1, sz, fp[1]); 707 if (i != j) { 708 differ = true; 709 i = MIN(i, j); 710 } 711 if (i == 0) 712 break; 713 for (k = 0; k < i; k++) { 714 if (!buf0[k] || !buf1[k]) 715 binary = true; 716 if (buf0[k] != buf1[k]) 717 differ = true; 718 } 719 } 720 if (differ) { 721 if (binary && !(option_mask32 & FLAG(a))) 722 status = STATUS_BINARY; 723 else if (diff(fp, file)) 724 status = STATUS_DIFFER; 725 } 726 if (status != STATUS_SAME) 727 exit_status |= 1; 728 out: 729 fclose_if_not_stdin(fp[0]); 730 fclose_if_not_stdin(fp[1]); 731 732 return status; 733 } 734 735 static void print_status(int status, char *path[2]) 736 { 737 switch (status) { 738 case STATUS_BINARY: 739 case STATUS_DIFFER: 740 if ((option_mask32 & FLAG(q)) || status == STATUS_BINARY) 741 printf("Files %s and %s differ\n", path[0], path[1]); 742 break; 743 case STATUS_SAME: 744 if (option_mask32 & FLAG(s)) 745 printf("Files %s and %s are identical\n", path[0], path[1]); 746 break; 747 } 748 } 1039 749 1040 750 #if ENABLE_FEATURE_DIFF_DIR 1041 static void do_diff(char *dir1, char *path1, char *dir2, char *path2) 1042 { 1043 int flags = D_HEADER; 1044 int val; 1045 char *fullpath1 = NULL; /* if -N */ 1046 char *fullpath2 = NULL; 1047 1048 if (path1) 1049 fullpath1 = concat_path_file(dir1, path1); 1050 if (path2) 1051 fullpath2 = concat_path_file(dir2, path2); 1052 1053 if (!fullpath1 || stat(fullpath1, &stb1) != 0) { 1054 flags |= D_EMPTY1; 1055 memset(&stb1, 0, sizeof(stb1)); 1056 if (path2) { 1057 free(fullpath1); 1058 fullpath1 = concat_path_file(dir1, path2); 1059 } 1060 } 1061 if (!fullpath2 || stat(fullpath2, &stb2) != 0) { 1062 flags |= D_EMPTY2; 1063 memset(&stb2, 0, sizeof(stb2)); 1064 stb2.st_mode = stb1.st_mode; 1065 if (path1) { 1066 free(fullpath2); 1067 fullpath2 = concat_path_file(dir2, path1); 1068 } 1069 } 1070 1071 if (stb1.st_mode == 0) 1072 stb1.st_mode = stb2.st_mode; 1073 1074 if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) { 1075 printf("Common subdirectories: %s and %s\n", fullpath1, fullpath2); 1076 goto ret; 1077 } 1078 1079 if (!S_ISREG(stb1.st_mode) && !S_ISDIR(stb1.st_mode)) 1080 val = D_SKIPPED1; 1081 else if (!S_ISREG(stb2.st_mode) && !S_ISDIR(stb2.st_mode)) 1082 val = D_SKIPPED2; 1083 else 1084 val = diffreg(fullpath1, fullpath2, flags); 1085 1086 print_status(val, fullpath1, fullpath2, NULL); 1087 ret: 1088 free(fullpath1); 1089 free(fullpath2); 1090 } 1091 #endif 1092 1093 1094 #if ENABLE_FEATURE_DIFF_DIR 1095 static int dir_strcmp(const void *p1, const void *p2) 1096 { 1097 return strcmp(*(char *const *) p1, *(char *const *) p2); 1098 } 1099 751 struct dlist { 752 size_t len; 753 int s, e; 754 char **dl; 755 }; 1100 756 1101 757 /* This function adds a filename to dl, the directory listing. */ 1102 static int add_to_dirlist(const char *filename, 1103 struct stat ATTRIBUTE_UNUSED * sb, void *userdata, 1104 int depth ATTRIBUTE_UNUSED) 1105 { 1106 /* +2: with space for eventual trailing NULL */ 1107 dl = xrealloc(dl, (dl_count+2) * sizeof(dl[0])); 1108 dl[dl_count] = xstrdup(filename + (int)(ptrdiff_t)userdata); 1109 dl_count++; 758 static int FAST_FUNC add_to_dirlist(const char *filename, 759 struct stat *sb UNUSED_PARAM, 760 void *userdata, int depth UNUSED_PARAM) 761 { 762 struct dlist *const l = userdata; 763 const char *file = filename + l->len; 764 while (*file == '/') 765 file++; 766 l->dl = xrealloc_vector(l->dl, 6, l->e); 767 l->dl[l->e] = xstrdup(file); 768 l->e++; 1110 769 return TRUE; 1111 770 } 1112 771 1113 1114 /* This returns a sorted directory listing. */ 1115 static char **get_dir(char *path) 1116 { 1117 dl_count = 0; 1118 dl = xzalloc(sizeof(dl[0])); 1119 1120 /* If -r has been set, then the recursive_action function will be 1121 * used. Unfortunately, this outputs the root directory along with 1122 * the recursed paths, so use void *userdata to specify the string 1123 * length of the root directory - '(void*)(strlen(path)+)'. 1124 * add_to_dirlist then removes root dir prefix. */ 1125 1126 if (option_mask32 & FLAG_r) { 1127 recursive_action(path, ACTION_RECURSE|ACTION_FOLLOWLINKS, 1128 add_to_dirlist, NULL, 1129 (void*)(strlen(path)+1), 0); 1130 } else { 1131 DIR *dp; 1132 struct dirent *ep; 1133 1134 dp = warn_opendir(path); 1135 while ((ep = readdir(dp))) { 1136 if (!strcmp(ep->d_name, "..") || LONE_CHAR(ep->d_name, '.')) 1137 continue; 1138 add_to_dirlist(ep->d_name, NULL, (void*)(int)0, 0); 1139 } 1140 closedir(dp); 1141 } 1142 1143 /* Sort dl alphabetically. */ 1144 qsort(dl, dl_count, sizeof(char *), dir_strcmp); 1145 1146 dl[dl_count] = NULL; 1147 return dl; 1148 } 1149 1150 1151 static void diffdir(char *p1, char *p2) 1152 { 1153 char **dirlist1, **dirlist2; 1154 char *dp1, *dp2; 1155 int pos; 1156 1157 /* Check for trailing slashes. */ 1158 dp1 = last_char_is(p1, '/'); 1159 if (dp1 != NULL) 1160 *dp1 = '\0'; 1161 dp2 = last_char_is(p2, '/'); 1162 if (dp2 != NULL) 1163 *dp2 = '\0'; 1164 1165 /* Get directory listings for p1 and p2. */ 1166 1167 dirlist1 = get_dir(p1); 1168 dirlist2 = get_dir(p2); 1169 1170 /* If -S was set, find the starting point. */ 1171 if (start) { 1172 while (*dirlist1 != NULL && strcmp(*dirlist1, start) < 0) 1173 dirlist1++; 1174 while (*dirlist2 != NULL && strcmp(*dirlist2, start) < 0) 1175 dirlist2++; 1176 if ((*dirlist1 == NULL) || (*dirlist2 == NULL)) 1177 bb_error_msg(bb_msg_invalid_arg, "NULL", "-S"); 1178 } 1179 772 /* If recursion is not set, this function adds the directory 773 * to the list and prevents recursive_action from recursing into it. 774 */ 775 static int FAST_FUNC skip_dir(const char *filename, 776 struct stat *sb, void *userdata, 777 int depth) 778 { 779 if (!(option_mask32 & FLAG(r)) && depth) { 780 add_to_dirlist(filename, sb, userdata, depth); 781 return SKIP; 782 } 783 if (!(option_mask32 & FLAG(N))) { 784 /* -r without -N: no need to recurse into dirs 785 * which do not exist on the "other side". 786 * Testcase: diff -r /tmp / 787 * (it would recurse deep into /proc without this code) */ 788 struct dlist *const l = userdata; 789 filename += l->len; 790 if (filename[0]) { 791 struct stat osb; 792 char *othername = concat_path_file(G.other_dir, filename); 793 int r = stat(othername, &osb); 794 free(othername); 795 if (r != 0 || !S_ISDIR(osb.st_mode)) { 796 /* other dir doesn't have similarly named 797 * directory, don't recurse */ 798 return SKIP; 799 } 800 } 801 } 802 return TRUE; 803 } 804 805 static void diffdir(char *p[2], const char *s_start) 806 { 807 struct dlist list[2]; 808 int i; 809 810 memset(&list, 0, sizeof(list)); 811 for (i = 0; i < 2; i++) { 812 /*list[i].s = list[i].e = 0; - memset did it */ 813 /*list[i].dl = NULL; */ 814 815 G.other_dir = p[1 - i]; 816 /* We need to trim root directory prefix. 817 * Using list.len to specify its length, 818 * add_to_dirlist will remove it. */ 819 list[i].len = strlen(p[i]); 820 recursive_action(p[i], ACTION_RECURSE | ACTION_FOLLOWLINKS, 821 add_to_dirlist, skip_dir, &list[i], 0); 822 /* Sort dl alphabetically. 823 * GNU diff does this ignoring any number of trailing dots. 824 * We don't, so for us dotted files almost always are 825 * first on the list. 826 */ 827 qsort_string_vector(list[i].dl, list[i].e); 828 /* If -S was set, find the starting point. */ 829 if (!s_start) 830 continue; 831 while (list[i].s < list[i].e && strcmp(list[i].dl[list[i].s], s_start) < 0) 832 list[i].s++; 833 } 1180 834 /* Now that both dirlist1 and dirlist2 contain sorted directory 1181 835 * listings, we can start to go through dirlist1. If both listings 1182 836 * contain the same file, then do a normal diff. Otherwise, behaviour 1183 837 * is determined by whether the -N flag is set. */ 1184 while (*dirlist1 != NULL || *dirlist2 != NULL) { 1185 dp1 = *dirlist1; 1186 dp2 = *dirlist2; 1187 pos = dp1 == NULL ? 1 : dp2 == NULL ? -1 : strcmp(dp1, dp2); 838 while (1) { 839 char *dp[2]; 840 int pos; 841 int k; 842 843 dp[0] = list[0].s < list[0].e ? list[0].dl[list[0].s] : NULL; 844 dp[1] = list[1].s < list[1].e ? list[1].dl[list[1].s] : NULL; 845 if (!dp[0] && !dp[1]) 846 break; 847 pos = !dp[0] ? 1 : (!dp[1] ? -1 : strcmp(dp[0], dp[1])); 848 k = pos > 0; 849 if (pos && !(option_mask32 & FLAG(N))) 850 printf("Only in %s: %s\n", p[k], dp[k]); 851 else { 852 char *fullpath[2], *path[2]; /* if -N */ 853 854 for (i = 0; i < 2; i++) { 855 if (pos == 0 || i == k) { 856 path[i] = fullpath[i] = concat_path_file(p[i], dp[i]); 857 stat(fullpath[i], &stb[i]); 858 } else { 859 fullpath[i] = concat_path_file(p[i], dp[1 - i]); 860 path[i] = (char *)bb_dev_null; 861 } 862 } 863 if (pos) 864 stat(fullpath[k], &stb[1 - k]); 865 866 if (S_ISDIR(stb[0].st_mode) && S_ISDIR(stb[1].st_mode)) 867 printf("Common subdirectories: %s and %s\n", fullpath[0], fullpath[1]); 868 else if (!S_ISREG(stb[0].st_mode) && !S_ISDIR(stb[0].st_mode)) 869 printf("File %s is not a regular file or directory and was skipped\n", fullpath[0]); 870 else if (!S_ISREG(stb[1].st_mode) && !S_ISDIR(stb[1].st_mode)) 871 printf("File %s is not a regular file or directory and was skipped\n", fullpath[1]); 872 else if (S_ISDIR(stb[0].st_mode) != S_ISDIR(stb[1].st_mode)) { 873 if (S_ISDIR(stb[0].st_mode)) 874 printf("File %s is a %s while file %s is a %s\n", fullpath[0], "directory", fullpath[1], "regular file"); 875 else 876 printf("File %s is a %s while file %s is a %s\n", fullpath[0], "regular file", fullpath[1], "directory"); 877 } else 878 print_status(diffreg(path), fullpath); 879 880 free(fullpath[0]); 881 free(fullpath[1]); 882 } 883 free(dp[k]); 884 list[k].s++; 1188 885 if (pos == 0) { 1189 do_diff(p1, dp1, p2, dp2); 1190 dirlist1++; 1191 dirlist2++; 1192 } else if (pos < 0) { 1193 if (option_mask32 & FLAG_N) 1194 do_diff(p1, dp1, p2, NULL); 1195 else 1196 print_only(p1, strlen(p1) + 1, dp1); 1197 dirlist1++; 1198 } else { 1199 if (option_mask32 & FLAG_N) 1200 do_diff(p1, NULL, p2, dp2); 1201 else 1202 print_only(p2, strlen(p2) + 1, dp2); 1203 dirlist2++; 1204 } 886 free(dp[1 - k]); 887 list[1 - k].s++; 888 } 889 } 890 if (ENABLE_FEATURE_CLEAN_UP) { 891 free(list[0].dl); 892 free(list[1].dl); 1205 893 } 1206 894 } 1207 895 #endif 1208 896 1209 1210 int diff_main(int argc, char **argv); 1211 int diff_main(int argc, char **argv) 1212 { 1213 bool gotstdin = 0; 1214 char *U_opt; 1215 char *f1, *f2; 897 #if ENABLE_FEATURE_DIFF_LONG_OPTIONS 898 static const char diff_longopts[] ALIGN1 = 899 "ignore-case\0" No_argument "i" 900 "ignore-tab-expansion\0" No_argument "E" 901 "ignore-space-change\0" No_argument "b" 902 "ignore-all-space\0" No_argument "w" 903 "ignore-blank-lines\0" No_argument "B" 904 "text\0" No_argument "a" 905 "unified\0" Required_argument "U" 906 "label\0" Required_argument "L" 907 "show-c-function\0" No_argument "p" 908 "brief\0" No_argument "q" 909 "expand-tabs\0" No_argument "t" 910 "initial-tab\0" No_argument "T" 911 "recursive\0" No_argument "r" 912 "new-file\0" No_argument "N" 913 "report-identical-files\0" No_argument "s" 914 "starting-file\0" Required_argument "S" 915 "minimal\0" No_argument "d" 916 ; 917 #endif 918 919 int diff_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 920 int diff_main(int argc UNUSED_PARAM, char **argv) 921 { 922 int gotstdin = 0, i; 923 char *file[2], *s_start = NULL; 1216 924 llist_t *L_arg = NULL; 1217 925 1218 926 INIT_G(); 1219 927 1220 /* exactly 2 params; collect multiple -L <label> */ 1221 opt_complementary = "=2:L::"; 1222 getopt32(argv, "abdiL:NqrsS:tTU:wu" 1223 "p" /* ignored (for compatibility) */, 1224 &L_arg, &start, &U_opt); 1225 /*argc -= optind;*/ 928 /* exactly 2 params; collect multiple -L <label>; -U N */ 929 opt_complementary = "=2:L::U+"; 930 #if ENABLE_FEATURE_DIFF_LONG_OPTIONS 931 applet_long_options = diff_longopts; 932 #endif 933 getopt32(argv, "abdiL:NqrsS:tTU:wupBE", 934 &L_arg, &s_start, &opt_U_context); 1226 935 argv += optind; 1227 while (L_arg) { 1228 if (label1 && label2) 1229 bb_show_usage(); 1230 if (!label1) 1231 label1 = L_arg->data; 1232 else { /* then label2 is NULL */ 1233 label2 = label1; 1234 label1 = L_arg->data; 1235 } 1236 /* we leak L_arg here... */ 1237 L_arg = L_arg->link; 1238 } 1239 if (option_mask32 & FLAG_U) 1240 context = xatoi_u(U_opt); 1241 1242 /* 1243 * Do sanity checks, fill in stb1 and stb2 and call the appropriate 1244 * driver routine. Both drivers use the contents of stb1 and stb2. 1245 */ 1246 1247 f1 = argv[0]; 1248 f2 = argv[1]; 1249 if (LONE_DASH(f1)) { 1250 fstat(STDIN_FILENO, &stb1); 1251 gotstdin = 1; 1252 } else 1253 xstat(f1, &stb1); 1254 if (LONE_DASH(f2)) { 1255 fstat(STDIN_FILENO, &stb2); 1256 gotstdin = 1; 1257 } else 1258 xstat(f2, &stb2); 1259 if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode))) 1260 bb_error_msg_and_die("can't compare - to a directory"); 1261 if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) { 936 while (L_arg) 937 label[!!label[0]] = llist_pop(&L_arg); 938 xfunc_error_retval = 2; 939 for (i = 0; i < 2; i++) { 940 file[i] = argv[i]; 941 /* Compat: "diff file name_which_doesnt_exist" exits with 2 */ 942 if (LONE_DASH(file[i])) { 943 fstat(STDIN_FILENO, &stb[i]); 944 gotstdin++; 945 } else 946 xstat(file[i], &stb[i]); 947 } 948 xfunc_error_retval = 1; 949 if (gotstdin && (S_ISDIR(stb[0].st_mode) || S_ISDIR(stb[1].st_mode))) 950 bb_error_msg_and_die("can't compare stdin to a directory"); 951 952 if (S_ISDIR(stb[0].st_mode) && S_ISDIR(stb[1].st_mode)) { 1262 953 #if ENABLE_FEATURE_DIFF_DIR 1263 diffdir(f 1, f2);954 diffdir(file, s_start); 1264 955 #else 1265 bb_error_msg_and_die(" directory comparison not supported");956 bb_error_msg_and_die("no support for directory comparison"); 1266 957 #endif 1267 958 } else { 1268 if (S_ISDIR(stb1.st_mode)) { 1269 f1 = concat_path_file(f1, f2); 1270 xstat(f1, &stb1); 1271 } 1272 if (S_ISDIR(stb2.st_mode)) { 1273 f2 = concat_path_file(f2, f1); 1274 xstat(f2, &stb2); 1275 } 1276 /* XXX: FIXME: */ 1277 /* We can't diff e.g. stdin supplied by a pipe - we use rewind(), fseek(). 1278 * This can be fixed (volunteers?) */ 1279 if (!S_ISREG(stb1.st_mode) || !S_ISREG(stb2.st_mode)) 1280 bb_error_msg_and_die("can't diff non-seekable stream"); 1281 print_status(diffreg(f1, f2, 0), f1, f2, NULL); 1282 } 1283 return status; 1284 } 959 bool dirfile = S_ISDIR(stb[0].st_mode) || S_ISDIR(stb[1].st_mode); 960 bool dir = S_ISDIR(stb[1].st_mode); 961 if (dirfile) { 962 const char *slash = strrchr(file[!dir], '/'); 963 file[dir] = concat_path_file(file[dir], slash ? slash + 1 : file[!dir]); 964 xstat(file[dir], &stb[dir]); 965 } 966 /* diffreg can get non-regular files here */ 967 print_status(gotstdin > 1 ? STATUS_SAME : diffreg(file), file); 968 969 if (dirfile) 970 free(file[dir]); 971 } 972 973 return exit_status; 974 } -
branches/2.2.9/mindi-busybox/editors/ed.c
r1765 r2725 9 9 10 10 #include "libbb.h" 11 12 #define searchString bb_common_bufsiz113 14 enum {15 USERSIZE = sizeof(searchString) > 1024 ? 102416 : sizeof(searchString) - 1, /* max line length typed in by user */17 INITBUF_SIZE = 1024, /* initial buffer size */18 };19 11 20 12 typedef struct LINE { … … 25 17 } LINE; 26 18 27 static LINE lines, *curLine; 28 static int curNum, lastNum, marks[26], dirty; 29 static char *bufBase, *bufPtr, *fileName; 30 static int bufUsed, bufSize; 19 20 #define searchString bb_common_bufsiz1 21 22 enum { 23 USERSIZE = sizeof(searchString) > 1024 ? 1024 24 : sizeof(searchString) - 1, /* max line length typed in by user */ 25 INITBUF_SIZE = 1024, /* initial buffer size */ 26 }; 27 28 struct globals { 29 int curNum; 30 int lastNum; 31 int bufUsed; 32 int bufSize; 33 LINE *curLine; 34 char *bufBase; 35 char *bufPtr; 36 char *fileName; 37 LINE lines; 38 smallint dirty; 39 int marks[26]; 40 }; 41 #define G (*ptr_to_globals) 42 #define curLine (G.curLine ) 43 #define bufBase (G.bufBase ) 44 #define bufPtr (G.bufPtr ) 45 #define fileName (G.fileName ) 46 #define curNum (G.curNum ) 47 #define lastNum (G.lastNum ) 48 #define bufUsed (G.bufUsed ) 49 #define bufSize (G.bufSize ) 50 #define dirty (G.dirty ) 51 #define lines (G.lines ) 52 #define marks (G.marks ) 53 #define INIT_G() do { \ 54 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 55 } while (0) 56 31 57 32 58 static void doCommands(void); 33 59 static void subCommand(const char *cmd, int num1, int num2); 34 static int getNum(const char **retcp, int *retHaveNum, int *retNum);60 static int getNum(const char **retcp, smallint *retHaveNum, int *retNum); 35 61 static int setCurNum(int num); 36 static int initEdit(void);37 static void termEdit(void);38 62 static void addLines(int num); 39 63 static int insertLine(int num, const char *data, int len); 40 static intdeleteLines(int num1, int num2);64 static void deleteLines(int num1, int num2); 41 65 static int printLines(int num1, int num2, int expandFlag); 42 66 static int writeLines(const char *file, int num1, int num2); … … 44 68 static int searchLines(const char *str, int num1, int num2); 45 69 static LINE *findLine(int num); 46 47 70 static int findString(const LINE *lp, const char * str, int len, int offset); 48 71 49 int ed_main(int argc, char **argv); 50 int ed_main(int argc, char **argv) 51 { 52 if (!initEdit()) 53 return EXIT_FAILURE; 54 55 if (argc > 1) { 56 fileName = strdup(argv[1]); 57 58 if (fileName == NULL) { 59 bb_error_msg("no memory"); 60 termEdit(); 72 73 static int bad_nums(int num1, int num2, const char *for_what) 74 { 75 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { 76 bb_error_msg("bad line range for %s", for_what); 77 return 1; 78 } 79 return 0; 80 } 81 82 83 static char *skip_blank(const char *cp) 84 { 85 while (isblank(*cp)) 86 cp++; 87 return (char *)cp; 88 } 89 90 91 int ed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 92 int ed_main(int argc UNUSED_PARAM, char **argv) 93 { 94 INIT_G(); 95 96 bufSize = INITBUF_SIZE; 97 bufBase = xmalloc(bufSize); 98 bufPtr = bufBase; 99 lines.next = &lines; 100 lines.prev = &lines; 101 102 if (argv[1]) { 103 fileName = xstrdup(argv[1]); 104 if (!readLines(fileName, 1)) { 61 105 return EXIT_SUCCESS; 62 106 } 63 64 if (!readLines(fileName, 1)) {65 termEdit();66 return EXIT_SUCCESS;67 }68 69 107 if (lastNum) 70 108 setCurNum(1); 71 72 109 dirty = FALSE; 73 110 } 74 111 75 112 doCommands(); 76 77 termEdit();78 113 return EXIT_SUCCESS; 79 114 } … … 85 120 { 86 121 const char *cp; 87 char *endbuf, *newname, buf[USERSIZE]; 88 int len, num1, num2, have1, have2; 122 char *endbuf, buf[USERSIZE]; 123 int len, num1, num2; 124 smallint have1, have2; 89 125 90 126 while (TRUE) { 91 printf(": "); 92 fflush(stdout); 93 94 if (fgets(buf, sizeof(buf), stdin) == NULL) 127 /* Returns: 128 * -1 on read errors or EOF, or on bare Ctrl-D. 129 * 0 on ctrl-C, 130 * >0 length of input string, including terminating '\n' 131 */ 132 len = read_line_input(": ", buf, sizeof(buf), NULL); 133 if (len <= 0) 95 134 return; 96 97 len = strlen(buf);98 99 if (len == 0)100 return;101 102 135 endbuf = &buf[len - 1]; 103 104 if (*endbuf != '\n') {105 bb_error_msg("command line too long");106 107 do {108 len = fgetc(stdin);109 } while ((len != EOF) && (len != '\n'));110 111 continue;112 }113 114 136 while ((endbuf > buf) && isblank(endbuf[-1])) 115 137 endbuf--; 116 117 138 *endbuf = '\0'; 118 139 119 cp = buf; 120 121 while (isblank(*cp)) 122 cp++; 123 140 cp = skip_blank(buf); 124 141 have1 = FALSE; 125 142 have2 = FALSE; … … 133 150 continue; 134 151 135 while (isblank(*cp)) 136 cp++; 152 cp = skip_blank(cp); 137 153 138 154 if (*cp == ',') { 139 155 cp++; 140 141 156 if (!getNum(&cp, &have2, &num2)) 142 157 continue; 143 144 158 if (!have1) 145 159 num1 = 1; 146 147 160 if (!have2) 148 161 num2 = lastNum; 149 150 162 have1 = TRUE; 151 163 have2 = TRUE; 152 164 } 153 154 165 if (!have1) 155 166 num1 = curNum; 156 157 167 if (!have2) 158 168 num2 = num1; 159 169 160 170 switch (*cp++) { 161 case 'a': 162 addLines(num1 + 1); 163 break; 164 165 case 'c': 166 deleteLines(num1, num2); 167 addLines(num1); 168 break; 169 170 case 'd': 171 deleteLines(num1, num2); 172 break; 173 174 case 'f': 175 if (*cp && !isblank(*cp)) { 176 bb_error_msg("bad file command"); 177 break; 178 } 179 180 while (isblank(*cp)) 181 cp++; 182 183 if (*cp == '\0') { 184 if (fileName) 185 printf("\"%s\"\n", fileName); 186 else 187 printf("No file name\n"); 188 break; 189 } 190 191 newname = strdup(cp); 192 193 if (newname == NULL) { 194 bb_error_msg("no memory for file name"); 195 break; 196 } 197 171 case 'a': 172 addLines(num1 + 1); 173 break; 174 175 case 'c': 176 deleteLines(num1, num2); 177 addLines(num1); 178 break; 179 180 case 'd': 181 deleteLines(num1, num2); 182 break; 183 184 case 'f': 185 if (*cp && !isblank(*cp)) { 186 bb_error_msg("bad file command"); 187 break; 188 } 189 cp = skip_blank(cp); 190 if (*cp == '\0') { 198 191 if (fileName) 199 free(fileName); 200 201 fileName = newname; 202 break; 203 204 case 'i': 205 addLines(num1); 206 break; 207 208 case 'k': 209 while (isblank(*cp)) 210 cp++; 211 212 if ((*cp < 'a') || (*cp > 'a') || cp[1]) { 213 bb_error_msg("bad mark name"); 214 break; 215 } 216 217 marks[*cp - 'a'] = num2; 218 break; 219 220 case 'l': 221 printLines(num1, num2, TRUE); 222 break; 223 224 case 'p': 225 printLines(num1, num2, FALSE); 226 break; 227 228 case 'q': 229 while (isblank(*cp)) 230 cp++; 231 232 if (have1 || *cp) { 233 bb_error_msg("bad quit command"); 234 break; 235 } 236 237 if (!dirty) 238 return; 239 240 printf("Really quit? "); 241 fflush(stdout); 242 243 buf[0] = '\0'; 244 fgets(buf, sizeof(buf), stdin); 245 cp = buf; 246 247 while (isblank(*cp)) 248 cp++; 249 250 if ((*cp == 'y') || (*cp == 'Y')) 251 return; 252 253 break; 254 255 case 'r': 256 if (*cp && !isblank(*cp)) { 257 bb_error_msg("bad read command"); 258 break; 259 } 260 261 while (isblank(*cp)) 262 cp++; 263 264 if (*cp == '\0') { 265 bb_error_msg("no file name"); 266 break; 267 } 268 269 if (!have1) 270 num1 = lastNum; 271 272 if (readLines(cp, num1 + 1)) 273 break; 274 275 if (fileName == NULL) 276 fileName = strdup(cp); 277 278 break; 279 280 case 's': 281 subCommand(cp, num1, num2); 282 break; 283 284 case 'w': 285 if (*cp && !isblank(*cp)) { 286 bb_error_msg("bad write command"); 287 break; 288 } 289 290 while (isblank(*cp)) 291 cp++; 292 293 if (!have1) { 294 num1 = 1; 295 num2 = lastNum; 296 } 297 298 if (*cp == '\0') 299 cp = fileName; 300 301 if (cp == NULL) { 302 bb_error_msg("no file name specified"); 303 break; 304 } 305 306 writeLines(cp, num1, num2); 307 break; 308 309 case 'z': 310 switch (*cp) { 311 case '-': 312 printLines(curNum-21, curNum, FALSE); 313 break; 314 case '.': 315 printLines(curNum-11, curNum+10, FALSE); 316 break; 317 default: 318 printLines(curNum, curNum+21, FALSE); 319 break; 320 } 321 break; 322 192 printf("\"%s\"\n", fileName); 193 else 194 printf("No file name\n"); 195 break; 196 } 197 free(fileName); 198 fileName = xstrdup(cp); 199 break; 200 201 case 'i': 202 addLines(num1); 203 break; 204 205 case 'k': 206 cp = skip_blank(cp); 207 if ((*cp < 'a') || (*cp > 'z') || cp[1]) { 208 bb_error_msg("bad mark name"); 209 break; 210 } 211 marks[*cp - 'a'] = num2; 212 break; 213 214 case 'l': 215 printLines(num1, num2, TRUE); 216 break; 217 218 case 'p': 219 printLines(num1, num2, FALSE); 220 break; 221 222 case 'q': 223 cp = skip_blank(cp); 224 if (have1 || *cp) { 225 bb_error_msg("bad quit command"); 226 break; 227 } 228 if (!dirty) 229 return; 230 len = read_line_input("Really quit? ", buf, 16, NULL); 231 /* read error/EOF - no way to continue */ 232 if (len < 0) 233 return; 234 cp = skip_blank(buf); 235 if ((*cp | 0x20) == 'y') /* Y or y */ 236 return; 237 break; 238 239 case 'r': 240 if (*cp && !isblank(*cp)) { 241 bb_error_msg("bad read command"); 242 break; 243 } 244 cp = skip_blank(cp); 245 if (*cp == '\0') { 246 bb_error_msg("no file name"); 247 break; 248 } 249 if (!have1) 250 num1 = lastNum; 251 if (readLines(cp, num1 + 1)) 252 break; 253 if (fileName == NULL) 254 fileName = xstrdup(cp); 255 break; 256 257 case 's': 258 subCommand(cp, num1, num2); 259 break; 260 261 case 'w': 262 if (*cp && !isblank(*cp)) { 263 bb_error_msg("bad write command"); 264 break; 265 } 266 cp = skip_blank(cp); 267 if (!have1) { 268 num1 = 1; 269 num2 = lastNum; 270 } 271 if (*cp == '\0') 272 cp = fileName; 273 if (cp == NULL) { 274 bb_error_msg("no file name specified"); 275 break; 276 } 277 writeLines(cp, num1, num2); 278 break; 279 280 case 'z': 281 switch (*cp) { 282 case '-': 283 printLines(curNum - 21, curNum, FALSE); 284 break; 323 285 case '.': 324 if (have1) { 325 bb_error_msg("no arguments allowed"); 326 break; 327 } 328 286 printLines(curNum - 11, curNum + 10, FALSE); 287 break; 288 default: 289 printLines(curNum, curNum + 21, FALSE); 290 break; 291 } 292 break; 293 294 case '.': 295 if (have1) { 296 bb_error_msg("no arguments allowed"); 297 break; 298 } 299 printLines(curNum, curNum, FALSE); 300 break; 301 302 case '-': 303 if (setCurNum(curNum - 1)) 329 304 printLines(curNum, curNum, FALSE); 330 break; 331 332 case '-': 333 if (setCurNum(curNum - 1)) 334 printLines(curNum, curNum, FALSE); 335 336 break; 337 338 case '=': 339 printf("%d\n", num1); 340 break; 341 342 case '\0': 343 if (have1) { 344 printLines(num2, num2, FALSE); 345 break; 346 } 347 348 if (setCurNum(curNum + 1)) 349 printLines(curNum, curNum, FALSE); 350 351 break; 352 353 default: 354 bb_error_msg("unimplemented command"); 355 break; 305 break; 306 307 case '=': 308 printf("%d\n", num1); 309 break; 310 case '\0': 311 if (have1) { 312 printLines(num2, num2, FALSE); 313 break; 314 } 315 if (setCurNum(curNum + 1)) 316 printLines(curNum, curNum, FALSE); 317 break; 318 319 default: 320 bb_error_msg("unimplemented command"); 321 break; 356 322 } 357 323 } … … 363 329 * The current line is set to the last substitution done. 364 330 */ 365 static void subCommand(const char * 331 static void subCommand(const char *cmd, int num1, int num2) 366 332 { 367 333 char *cp, *oldStr, *newStr, buf[USERSIZE]; … … 370 336 int globalFlag, printFlag, didSub, needPrint; 371 337 372 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { 373 bb_error_msg("bad line range for substitute"); 338 if (bad_nums(num1, num2, "substitute")) 374 339 return; 375 }376 340 377 341 globalFlag = FALSE; … … 395 359 396 360 cp = strchr(cp, delim); 397 398 361 if (cp == NULL) { 399 362 bb_error_msg("missing 2nd delimiter for substitute"); … … 415 378 globalFlag = TRUE; 416 379 break; 417 418 380 case 'p': 419 381 printFlag = TRUE; 420 382 break; 421 422 383 default: 423 384 bb_error_msg("unknown option for substitute"); … … 430 391 return; 431 392 } 432 433 393 oldStr = searchString; 434 394 } … … 438 398 439 399 lp = findLine(num1); 440 441 400 if (lp == NULL) 442 401 return; … … 456 415 needPrint = FALSE; 457 416 } 458 459 417 offset = 0; 460 418 lp = lp->next; 461 419 num1++; 462 463 420 continue; 464 421 } … … 474 431 if (deltaLen <= 0) { 475 432 memcpy(&lp->data[offset], newStr, newLen); 476 477 433 if (deltaLen) { 478 434 memcpy(&lp->data[offset + newLen], … … 482 438 lp->len += deltaLen; 483 439 } 484 485 440 offset += newLen; 486 487 441 if (globalFlag) 488 442 continue; 489 490 443 if (needPrint) { 491 444 printLines(num1, num1, FALSE); 492 445 needPrint = FALSE; 493 446 } 494 495 447 lp = lp->next; 496 448 num1++; 497 498 449 continue; 499 450 } … … 504 455 * the old line structure. 505 456 */ 506 nlp = (LINE *) malloc(sizeof(LINE) + lp->len + deltaLen); 507 508 if (nlp == NULL) { 509 bb_error_msg("cannot get memory for line"); 510 return; 511 } 457 nlp = xmalloc(sizeof(LINE) + lp->len + deltaLen); 512 458 513 459 nlp->len = lp->len + deltaLen; 514 460 515 461 memcpy(nlp->data, lp->data, offset); 516 517 462 memcpy(&nlp->data[offset], newStr, newLen); 518 519 463 memcpy(&nlp->data[offset + newLen], 520 464 &lp->data[offset + oldLen], … … 555 499 * offset in the line. Returns the offset of the found string, or -1. 556 500 */ 557 static int findString( const LINE * lp, const char *str, int len, int offset)501 static int findString(const LINE *lp, const char *str, int len, int offset) 558 502 { 559 503 int left; … … 565 509 while (left >= len) { 566 510 ncp = memchr(cp, *str, left); 567 568 511 if (ncp == NULL) 569 512 return -1; 570 571 513 left -= (ncp - cp); 572 573 514 if (left < len) 574 515 return -1; 575 576 516 cp = ncp; 577 578 517 if (memcmp(cp, str, len) == 0) 579 518 return (cp - lp->data); 580 581 519 cp++; 582 520 left--; … … 598 536 char buf[USERSIZE + 1]; 599 537 600 while (fgets(buf, sizeof(buf), stdin)) { 538 while (1) { 539 /* Returns: 540 * -1 on read errors or EOF, or on bare Ctrl-D. 541 * 0 on ctrl-C, 542 * >0 length of input string, including terminating '\n' 543 */ 544 len = read_line_input("", buf, sizeof(buf), NULL); 545 if (len <= 0) { 546 /* Previously, ctrl-C was exiting to shell. 547 * Now we exit to ed prompt. Is in important? */ 548 return; 549 } 601 550 if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0')) 602 551 return; 603 604 len = strlen(buf);605 606 if (len == 0)607 return;608 609 if (buf[len - 1] != '\n') {610 bb_error_msg("line too long");611 do {612 len = fgetc(stdin);613 } while ((len != EOF) && (len != '\n'));614 return;615 }616 617 552 if (!insertLine(num++, buf, len)) 618 553 return; … … 629 564 * The character pointer which stopped the scan is also returned. 630 565 */ 631 static int getNum(const char **retcp, int *retHaveNum, int *retNum)566 static int getNum(const char **retcp, smallint *retHaveNum, int *retNum) 632 567 { 633 568 const char *cp; 634 569 char *endStr, str[USERSIZE]; 635 int haveNum, value, num, sign; 570 int value, num; 571 smallint haveNum, minus; 636 572 637 573 cp = *retcp; 574 value = 0; 638 575 haveNum = FALSE; 639 value = 0; 640 sign = 1; 576 minus = 0; 641 577 642 578 while (TRUE) { 643 while (isblank(*cp)) 644 cp++; 579 cp = skip_blank(cp); 645 580 646 581 switch (*cp) { … … 659 594 case '\'': 660 595 cp++; 661 662 596 if ((*cp < 'a') || (*cp > 'z')) { 663 597 bb_error_msg("bad mark name"); 664 598 return FALSE; 665 599 } 666 667 600 haveNum = TRUE; 668 601 num = marks[*cp++ - 'a']; … … 672 605 strcpy(str, ++cp); 673 606 endStr = strchr(str, '/'); 674 675 607 if (endStr) { 676 608 *endStr++ = '\0'; 677 609 cp += (endStr - str); 678 } 679 else 610 } else 680 611 cp = ""; 681 682 612 num = searchLines(str, curNum, lastNum); 683 684 613 if (num == 0) 685 614 return FALSE; 686 687 615 haveNum = TRUE; 688 616 break; … … 695 623 return TRUE; 696 624 } 697 698 625 num = 0; 699 700 626 while (isdigit(*cp)) 701 627 num = num * 10 + *cp++ - '0'; 702 703 628 haveNum = TRUE; 704 629 break; 705 630 } 706 631 707 value += num * sign; 708 709 while (isblank(*cp)) 710 cp++; 632 value += (minus ? -num : num); 633 634 cp = skip_blank(cp); 711 635 712 636 switch (*cp) { 713 637 case '-': 714 sign = -1;638 minus = 1; 715 639 cp++; 716 640 break; 717 641 718 642 case '+': 719 sign = 1;643 minus = 0; 720 644 cp++; 721 645 break; … … 732 656 733 657 /* 734 * Initialize everything for editing.735 */736 static int initEdit(void)737 {738 int i;739 740 bufSize = INITBUF_SIZE;741 bufBase = malloc(bufSize);742 743 if (bufBase == NULL) {744 bb_error_msg("no memory for buffer");745 return FALSE;746 }747 748 bufPtr = bufBase;749 bufUsed = 0;750 751 lines.next = &lines;752 lines.prev = &lines;753 754 curLine = NULL;755 curNum = 0;756 lastNum = 0;757 dirty = FALSE;758 fileName = NULL;759 searchString[0] = '\0';760 761 for (i = 0; i < 26; i++)762 marks[i] = 0;763 764 return TRUE;765 }766 767 768 /*769 * Finish editing.770 */771 static void termEdit(void)772 {773 if (bufBase)774 free(bufBase);775 776 bufBase = NULL;777 bufPtr = NULL;778 bufSize = 0;779 bufUsed = 0;780 781 if (fileName)782 free(fileName);783 784 fileName = NULL;785 786 searchString[0] = '\0';787 788 if (lastNum)789 deleteLines(1, lastNum);790 791 lastNum = 0;792 curNum = 0;793 curLine = NULL;794 }795 796 797 /*798 658 * Read lines from a file at the specified line number. 799 659 * Returns TRUE if the file was successfully read. 800 660 */ 801 static int readLines(const char * 661 static int readLines(const char *file, int num) 802 662 { 803 663 int fd, cc; … … 811 671 812 672 fd = open(file, 0); 813 814 673 if (fd < 0) { 815 perror(file);674 bb_simple_perror_msg(file); 816 675 return FALSE; 817 676 } … … 824 683 825 684 printf("\"%s\", ", file); 826 fflush (stdout);685 fflush_all(); 827 686 828 687 do { … … 831 690 if (cp) { 832 691 len = (cp - bufPtr) + 1; 833 834 692 if (!insertLine(num, bufPtr, len)) { 835 693 close(fd); 836 694 return FALSE; 837 695 } 838 839 696 bufPtr += len; 840 697 bufUsed -= len; … … 842 699 lineCount++; 843 700 num++; 844 845 701 continue; 846 702 } … … 853 709 if (bufUsed >= bufSize) { 854 710 len = (bufSize * 3) / 2; 855 cp = realloc(bufBase, len); 856 857 if (cp == NULL) { 858 bb_error_msg("no memory for buffer"); 859 close(fd); 860 return FALSE; 861 } 862 711 cp = xrealloc(bufBase, len); 863 712 bufBase = cp; 864 713 bufPtr = bufBase + bufUsed; … … 866 715 } 867 716 868 cc = read(fd, bufPtr, bufSize - bufUsed);717 cc = safe_read(fd, bufPtr, bufSize - bufUsed); 869 718 bufUsed += cc; 870 719 bufPtr = bufBase; … … 873 722 874 723 if (cc < 0) { 875 perror(file);724 bb_simple_perror_msg(file); 876 725 close(fd); 877 726 return FALSE; … … 883 732 return -1; 884 733 } 885 886 734 lineCount++; 887 735 charCount += bufUsed; … … 901 749 * Returns TRUE if successful, or FALSE on an error with a message output. 902 750 */ 903 static int writeLines(const char * 751 static int writeLines(const char *file, int num1, int num2) 904 752 { 905 753 LINE *lp; 906 754 int fd, lineCount, charCount; 907 755 908 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { 909 bb_error_msg("bad line range for write"); 756 if (bad_nums(num1, num2, "write")) 910 757 return FALSE; 911 }912 758 913 759 lineCount = 0; … … 915 761 916 762 fd = creat(file, 0666); 917 918 763 if (fd < 0) { 919 perror(file);764 bb_simple_perror_msg(file); 920 765 return FALSE; 921 766 } 922 767 923 768 printf("\"%s\", ", file); 924 fflush (stdout);769 fflush_all(); 925 770 926 771 lp = findLine(num1); 927 928 772 if (lp == NULL) { 929 773 close(fd); … … 932 776 933 777 while (num1++ <= num2) { 934 if ( write(fd, lp->data, lp->len) != lp->len) {935 perror(file);778 if (full_write(fd, lp->data, lp->len) != lp->len) { 779 bb_simple_perror_msg(file); 936 780 close(fd); 937 781 return FALSE; 938 782 } 939 940 783 charCount += lp->len; 941 784 lineCount++; … … 944 787 945 788 if (close(fd) < 0) { 946 perror(file);789 bb_simple_perror_msg(file); 947 790 return FALSE; 948 791 } … … 965 808 int ch, count; 966 809 967 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { 968 bb_error_msg("bad line range for print"); 810 if (bad_nums(num1, num2, "print")) 969 811 return FALSE; 970 }971 812 972 813 lp = findLine(num1); 973 974 814 if (lp == NULL) 975 815 return FALSE; … … 977 817 while (num1 <= num2) { 978 818 if (!expandFlag) { 979 write( 1, lp->data, lp->len);819 write(STDOUT_FILENO, lp->data, lp->len); 980 820 setCurNum(num1++); 981 821 lp = lp->next; 982 983 822 continue; 984 823 } … … 995 834 996 835 while (count-- > 0) { 997 ch = *cp++; 998 999 if (ch & 0x80) { 1000 fputs("M-", stdout); 1001 ch &= 0x7f; 1002 } 1003 1004 if (ch < ' ') { 1005 fputc('^', stdout); 1006 ch += '@'; 1007 } 1008 1009 if (ch == 0x7f) { 1010 fputc('^', stdout); 1011 ch = '?'; 1012 } 1013 1014 fputc(ch, stdout); 836 ch = (unsigned char) *cp++; 837 fputc_printable(ch | PRINTABLE_META, stdout); 1015 838 } 1016 839 … … 1032 855 * Returns TRUE if successful. 1033 856 */ 1034 static int insertLine(int num, const char * 857 static int insertLine(int num, const char *data, int len) 1035 858 { 1036 859 LINE *newLp, *lp; … … 1041 864 } 1042 865 1043 newLp = malloc(sizeof(LINE) + len - 1); 1044 1045 if (newLp == NULL) { 1046 bb_error_msg("failed to allocate memory for line"); 1047 return FALSE; 1048 } 866 newLp = xmalloc(sizeof(LINE) + len - 1); 1049 867 1050 868 memcpy(newLp->data, data, len); … … 1055 873 else { 1056 874 lp = findLine(num); 1057 1058 875 if (lp == NULL) { 1059 876 free((char *) newLp); … … 1076 893 * Delete lines from the given range. 1077 894 */ 1078 static intdeleteLines(int num1, int num2)895 static void deleteLines(int num1, int num2) 1079 896 { 1080 897 LINE *lp, *nlp, *plp; 1081 898 int count; 1082 899 1083 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { 1084 bb_error_msg("bad line numbers for delete"); 1085 return FALSE; 1086 } 900 if (bad_nums(num1, num2, "delete")) 901 return; 1087 902 1088 903 lp = findLine(num1); 1089 1090 904 if (lp == NULL) 1091 return FALSE;905 return; 1092 906 1093 907 if ((curNum >= num1) && (curNum <= num2)) { … … 1101 915 1102 916 count = num2 - num1 + 1; 1103 1104 917 if (curNum > num2) 1105 918 curNum -= count; 1106 1107 919 lastNum -= count; 1108 920 … … 1112 924 plp->next = nlp; 1113 925 nlp->prev = plp; 1114 lp->next = NULL;1115 lp->prev = NULL;1116 lp->len = 0;1117 926 free(lp); 1118 927 lp = nlp; … … 1120 929 1121 930 dirty = TRUE; 1122 1123 return TRUE;1124 931 } 1125 932 … … 1127 934 /* 1128 935 * Search for a line which contains the specified string. 1129 * If the string is NULL, then the previously searched for string936 * If the string is "", then the previously searched for string 1130 937 * is used. The currently searched for string is saved for future use. 1131 938 * Returns the line number which matches, or 0 if there was no match 1132 939 * with an error printed. 1133 940 */ 1134 static int searchLines(const char *str, int num1, int num2)941 static NOINLINE int searchLines(const char *str, int num1, int num2) 1135 942 { 1136 943 const LINE *lp; 1137 944 int len; 1138 945 1139 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { 1140 bb_error_msg("bad line numbers for search"); 946 if (bad_nums(num1, num2, "search")) 1141 947 return 0; 1142 }1143 948 1144 949 if (*str == '\0') { … … 1147 952 return 0; 1148 953 } 1149 1150 954 str = searchString; 1151 955 } … … 1157 961 1158 962 lp = findLine(num1); 1159 1160 963 if (lp == NULL) 1161 964 return 0; … … 1164 967 if (findString(lp, str, len, 0) >= 0) 1165 968 return num1; 1166 1167 969 num1++; 1168 970 lp = lp->next; 1169 971 } 1170 972 1171 bb_error_msg("can not find string \"%s\"", str);973 bb_error_msg("can't find string \"%s\"", str); 1172 974 return 0; 1173 975 } … … 1197 999 lp = curLine; 1198 1000 lnum = curNum; 1199 1200 1001 if (num < (curNum / 2)) { 1201 1002 lp = lines.next; 1202 1003 lnum = 1; 1203 } 1204 else if (num > ((curNum + lastNum) / 2)) { 1004 } else if (num > ((curNum + lastNum) / 2)) { 1205 1005 lp = lines.prev; 1206 1006 lnum = lastNum; … … 1229 1029 1230 1030 lp = findLine(num); 1231 1232 1031 if (lp == NULL) 1233 1032 return FALSE; 1234 1235 1033 curNum = num; 1236 1034 curLine = lp; -
branches/2.2.9/mindi-busybox/editors/patch.c
r1765 r2725 1 /* vi: set sw=4 ts=4: */ 2 /* 3 * busybox patch applet to handle the unified diff format. 4 * Copyright (C) 2003 Glenn McGrath <bug1@iinet.net.au> 1 /* vi: set sw=4 ts=4: 5 2 * 6 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. 3 * Apply a "universal" diff. 4 * Adapted from toybox's patch implementation. 7 5 * 8 * This applet is written to work with patches generated by GNU diff, 9 * where there is equivalent functionality busybox patch shall behave 10 * as per GNU patch. 6 * Copyright 2007 Rob Landley <rob@landley.net> 11 7 * 12 * There is a SUSv3 specification for patch, however it looks to be 13 * incomplete, it doesnt even mention unified diff format. 14 * http://www.opengroup.org/onlinepubs/007904975/utilities/patch.html 8 * see http://www.opengroup.org/onlinepubs/009695399/utilities/patch.html 9 * (But only does -u, because who still cares about "ed"?) 15 10 * 16 * Issues 17 * - Non-interactive 18 * - Patches must apply cleanly or patch (not just one hunk) will fail. 19 * - Reject file isnt saved 11 * TODO: 12 * -b backup 13 * -l treat all whitespace as a single space 14 * -d chdir first 15 * -D define wrap #ifdef and #ifndef around changes 16 * -o outfile output here instead of in place 17 * -r rejectfile write rejected hunks to this file 18 * 19 * -f force (no questions asked) 20 * -F fuzz (number, default 2) 21 * [file] which file to patch 20 22 */ 21 23 22 #include <getopt.h> 24 //applet:IF_PATCH(APPLET(patch, _BB_DIR_USR_BIN, _BB_SUID_DROP)) 25 26 //kbuild:lib-$(CONFIG_PATCH) += patch.o 27 28 //config:config PATCH 29 //config: bool "patch" 30 //config: default y 31 //config: help 32 //config: Apply a unified diff formatted patch. 33 34 //usage:#define patch_trivial_usage 35 //usage: "[OPTIONS] [ORIGFILE [PATCHFILE]]" 36 //usage:#define patch_full_usage "\n\n" 37 //usage: IF_LONG_OPTS( 38 //usage: " -p,--strip N Strip N leading components from file names" 39 //usage: "\n -i,--input DIFF Read DIFF instead of stdin" 40 //usage: "\n -R,--reverse Reverse patch" 41 //usage: "\n -N,--forward Ignore already applied patches" 42 //usage: "\n --dry-run Don't actually change files" 43 //usage: "\n -E,--remove-empty-files Remove output files if they become empty" 44 //usage: ) 45 //usage: IF_NOT_LONG_OPTS( 46 //usage: " -p N Strip N leading components from file names" 47 //usage: "\n -i DIFF Read DIFF instead of stdin" 48 //usage: "\n -R Reverse patch" 49 //usage: "\n -N Ignore already applied patches" 50 //usage: "\n -E Remove output files if they become empty" 51 //usage: ) 52 //usage: 53 //usage:#define patch_example_usage 54 //usage: "$ patch -p1 < example.diff\n" 55 //usage: "$ patch -p0 -i example.diff" 23 56 24 57 #include "libbb.h" 25 58 26 static unsigned int copy_lines(FILE *src_stream, FILE *dest_stream, const unsigned int lines_count) 27 { 28 unsigned int i = 0; 29 30 while (src_stream && (i < lines_count)) { 31 char *line; 32 line = xmalloc_fgets(src_stream); 33 if (line == NULL) { 34 break; 35 } 36 if (fputs(line, dest_stream) == EOF) { 37 bb_perror_msg_and_die("error writing to new file"); 38 } 39 free(line); 40 41 i++; 42 } 43 return i; 44 } 45 46 /* If patch_level is -1 it will remove all directory names 47 * char *line must be greater than 4 chars 48 * returns NULL if the file doesnt exist or error 49 * returns malloc'ed filename 50 */ 51 52 static char *extract_filename(char *line, int patch_level) 53 { 54 char *temp, *filename_start_ptr = line + 4; 55 int i; 56 57 /* Terminate string at end of source filename */ 58 temp = strchrnul(filename_start_ptr, '\t'); 59 *temp = '\0'; 60 61 /* Skip over (patch_level) number of leading directories */ 62 if (patch_level == -1) 63 patch_level = INT_MAX; 64 for (i = 0; i < patch_level; i++) { 65 temp = strchr(filename_start_ptr, '/'); 66 if (!temp) 67 break; 68 filename_start_ptr = temp + 1; 69 } 70 71 return xstrdup(filename_start_ptr); 72 } 73 74 static int file_doesnt_exist(const char *filename) 75 { 76 struct stat statbuf; 77 return stat(filename, &statbuf); 78 } 79 80 int patch_main(int argc, char **argv); 81 int patch_main(int argc, char **argv) 82 { 83 int patch_level = -1; 84 char *patch_line; 85 int ret; 86 FILE *patch_file = NULL; 87 88 { 89 char *p, *i; 90 ret = getopt32(argv, "p:i:", &p, &i); 91 if (ret & 1) 92 patch_level = xatol_range(p, -1, USHRT_MAX); 93 if (ret & 2) { 94 patch_file = xfopen(i, "r"); 95 } else { 96 patch_file = stdin; 97 } 98 ret = 0; 99 } 100 101 patch_line = xmalloc_getline(patch_file); 102 while (patch_line) { 103 FILE *src_stream; 104 FILE *dst_stream; 105 char *original_filename; 106 char *new_filename; 107 char *backup_filename; 108 unsigned int src_cur_line = 1; 109 unsigned int dest_cur_line = 0; 110 unsigned int dest_beg_line; 111 unsigned int bad_hunk_count = 0; 112 unsigned int hunk_count = 0; 113 char copy_trailing_lines_flag = 0; 114 115 /* Skip everything upto the "---" marker 116 * No need to parse the lines "Only in <dir>", and "diff <args>" 117 */ 118 while (patch_line && strncmp(patch_line, "--- ", 4) != 0) { 119 free(patch_line); 120 patch_line = xmalloc_getline(patch_file); 121 } 122 /* FIXME: patch_line NULL check?? */ 123 124 /* Extract the filename used before the patch was generated */ 125 original_filename = extract_filename(patch_line, patch_level); 126 free(patch_line); 127 128 patch_line = xmalloc_getline(patch_file); 129 /* FIXME: NULL check?? */ 130 if (strncmp(patch_line, "+++ ", 4) != 0) { 131 ret = 2; 132 bb_error_msg("invalid patch"); 133 continue; 134 } 135 new_filename = extract_filename(patch_line, patch_level); 136 free(patch_line); 137 138 if (file_doesnt_exist(new_filename)) { 139 char *line_ptr; 140 /* Create leading directories */ 141 line_ptr = strrchr(new_filename, '/'); 142 if (line_ptr) { 143 *line_ptr = '\0'; 144 bb_make_directory(new_filename, -1, FILEUTILS_RECUR); 145 *line_ptr = '/'; 59 60 // libbb candidate? 61 62 struct double_list { 63 struct double_list *next; 64 struct double_list *prev; 65 char *data; 66 }; 67 68 // Free all the elements of a linked list 69 // Call freeit() on each element before freeing it. 70 static 71 void dlist_free(struct double_list *list, void (*freeit)(void *data)) 72 { 73 while (list) { 74 void *pop = list; 75 list = list->next; 76 freeit(pop); 77 // Bail out also if list is circular. 78 if (list == pop) break; 79 } 80 } 81 82 // Add an entry before "list" element in (circular) doubly linked list 83 static 84 struct double_list *dlist_add(struct double_list **list, char *data) 85 { 86 struct double_list *llist; 87 struct double_list *line = xmalloc(sizeof(*line)); 88 89 line->data = data; 90 llist = *list; 91 if (llist) { 92 struct double_list *p; 93 line->next = llist; 94 p = line->prev = llist->prev; 95 // (list is circular, we assume p is never NULL) 96 p->next = line; 97 llist->prev = line; 98 } else 99 *list = line->next = line->prev = line; 100 101 return line; 102 } 103 104 105 struct globals { 106 char *infile; 107 long prefix; 108 109 struct double_list *current_hunk; 110 111 long oldline, oldlen, newline, newlen; 112 long linenum; 113 int context, state, hunknum; 114 int filein, fileout; 115 char *tempname; 116 117 int exitval; 118 }; 119 #define TT (*ptr_to_globals) 120 #define INIT_TT() do { \ 121 SET_PTR_TO_GLOBALS(xzalloc(sizeof(TT))); \ 122 } while (0) 123 124 125 #define FLAG_STR "Rup:i:NEx" 126 /* FLAG_REVERSE must be == 1! Code uses this fact. */ 127 #define FLAG_REVERSE (1 << 0) 128 #define FLAG_u (1 << 1) 129 #define FLAG_PATHLEN (1 << 2) 130 #define FLAG_INPUT (1 << 3) 131 #define FLAG_IGNORE (1 << 4) 132 #define FLAG_RMEMPTY (1 << 5) 133 //non-standard: 134 #define FLAG_DEBUG (1 << 6) 135 136 // Dispose of a line of input, either by writing it out or discarding it. 137 138 // state < 2: just free 139 // state = 2: write whole line to stderr 140 // state = 3: write whole line to fileout 141 // state > 3: write line+1 to fileout when *line != state 142 143 #define PATCH_DEBUG (option_mask32 & FLAG_DEBUG) 144 145 static void do_line(void *data) 146 { 147 struct double_list *dlist = data; 148 149 if (TT.state>1 && *dlist->data != TT.state) 150 fdprintf(TT.state == 2 ? 2 : TT.fileout, 151 "%s\n", dlist->data+(TT.state>3 ? 1 : 0)); 152 153 if (PATCH_DEBUG) fdprintf(2, "DO %d: %s\n", TT.state, dlist->data); 154 155 free(dlist->data); 156 free(dlist); 157 } 158 159 static void finish_oldfile(void) 160 { 161 if (TT.tempname) { 162 // Copy the rest of the data and replace the original with the copy. 163 char *temp; 164 165 if (TT.filein != -1) { 166 bb_copyfd_eof(TT.filein, TT.fileout); 167 xclose(TT.filein); 168 } 169 xclose(TT.fileout); 170 171 temp = xstrdup(TT.tempname); 172 temp[strlen(temp) - 6] = '\0'; 173 rename(TT.tempname, temp); 174 free(temp); 175 176 free(TT.tempname); 177 TT.tempname = NULL; 178 } 179 TT.fileout = TT.filein = -1; 180 } 181 182 static void fail_hunk(void) 183 { 184 if (!TT.current_hunk) return; 185 186 fdprintf(2, "Hunk %d FAILED %ld/%ld.\n", TT.hunknum, TT.oldline, TT.newline); 187 TT.exitval = 1; 188 189 // If we got to this point, we've seeked to the end. Discard changes to 190 // this file and advance to next file. 191 192 TT.state = 2; 193 TT.current_hunk->prev->next = NULL; 194 dlist_free(TT.current_hunk, do_line); 195 TT.current_hunk = NULL; 196 197 // Abort the copy and delete the temporary file. 198 close(TT.filein); 199 close(TT.fileout); 200 unlink(TT.tempname); 201 free(TT.tempname); 202 TT.tempname = NULL; 203 204 TT.state = 0; 205 } 206 207 // Given a hunk of a unified diff, make the appropriate change to the file. 208 // This does not use the location information, but instead treats a hunk 209 // as a sort of regex. Copies data from input to output until it finds 210 // the change to be made, then outputs the changed data and returns. 211 // (Finding EOF first is an error.) This is a single pass operation, so 212 // multiple hunks must occur in order in the file. 213 214 static int apply_one_hunk(void) 215 { 216 struct double_list *plist, *buf = NULL, *check; 217 int matcheof = 0, reverse = option_mask32 & FLAG_REVERSE, backwarn = 0; 218 /* Do we try "dummy" revert to check whether 219 * to silently skip this hunk? Used to implement -N. 220 */ 221 int dummy_revert = 0; 222 223 // Break doubly linked list so we can use singly linked traversal function. 224 TT.current_hunk->prev->next = NULL; 225 226 // Match EOF if there aren't as many ending context lines as beginning 227 for (plist = TT.current_hunk; plist; plist = plist->next) { 228 if (plist->data[0]==' ') matcheof++; 229 else matcheof = 0; 230 if (PATCH_DEBUG) fdprintf(2, "HUNK:%s\n", plist->data); 231 } 232 matcheof = matcheof < TT.context; 233 234 if (PATCH_DEBUG) fdprintf(2,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N'); 235 236 // Loop through input data searching for this hunk. Match all context 237 // lines and all lines to be removed until we've found the end of a 238 // complete hunk. 239 plist = TT.current_hunk; 240 buf = NULL; 241 if (TT.context) for (;;) { 242 char *data = xmalloc_reads(TT.filein, NULL, NULL); 243 244 TT.linenum++; 245 246 // Figure out which line of hunk to compare with next. (Skip lines 247 // of the hunk we'd be adding.) 248 while (plist && *plist->data == "+-"[reverse]) { 249 if (data && !strcmp(data, plist->data+1)) { 250 if (!backwarn) { 251 backwarn = TT.linenum; 252 if (option_mask32 & FLAG_IGNORE) { 253 dummy_revert = 1; 254 reverse ^= 1; 255 continue; 256 } 257 } 146 258 } 147 dst_stream = xfopen(new_filename, "w+"); 148 backup_filename = NULL; 149 } else { 150 backup_filename = xmalloc(strlen(new_filename) + 6); 151 strcpy(backup_filename, new_filename); 152 strcat(backup_filename, ".orig"); 153 if (rename(new_filename, backup_filename) == -1) { 154 bb_perror_msg_and_die("cannot create file %s", 155 backup_filename); 156 } 157 dst_stream = xfopen(new_filename, "w"); 158 } 159 160 if ((backup_filename == NULL) || file_doesnt_exist(original_filename)) { 161 src_stream = NULL; 162 } else { 163 if (strcmp(original_filename, new_filename) == 0) { 164 src_stream = xfopen(backup_filename, "r"); 165 } else { 166 src_stream = xfopen(original_filename, "r"); 167 } 168 } 169 170 printf("patching file %s\n", new_filename); 171 172 /* Handle each hunk */ 173 patch_line = xmalloc_fgets(patch_file); 174 while (patch_line) { 175 unsigned int count; 176 unsigned int src_beg_line; 177 unsigned int unused; 178 unsigned int hunk_offset_start = 0; 179 int hunk_error = 0; 180 181 /* This bit should be improved */ 182 if ((sscanf(patch_line, "@@ -%d,%d +%d,%d @@", &src_beg_line, &unused, &dest_beg_line, &unused) != 4) && 183 (sscanf(patch_line, "@@ -%d,%d +%d @@", &src_beg_line, &unused, &dest_beg_line) != 3) && 184 (sscanf(patch_line, "@@ -%d +%d,%d @@", &src_beg_line, &dest_beg_line, &unused) != 3)) { 185 /* No more hunks for this file */ 186 break; 187 } 188 free(patch_line); 189 hunk_count++; 190 191 if (src_beg_line && dest_beg_line) { 192 /* Copy unmodified lines upto start of hunk */ 193 /* src_beg_line will be 0 if its a new file */ 194 count = src_beg_line - src_cur_line; 195 if (copy_lines(src_stream, dst_stream, count) != count) { 196 bb_error_msg_and_die("bad src file"); 197 } 198 src_cur_line += count; 199 dest_cur_line += count; 200 copy_trailing_lines_flag = 1; 201 } 202 hunk_offset_start = src_cur_line; 203 204 while ((patch_line = xmalloc_fgets(patch_file)) != NULL) { 205 if ((*patch_line == '-') || (*patch_line == ' ')) { 206 char *src_line = NULL; 207 if (src_stream) { 208 src_line = xmalloc_fgets(src_stream); 209 if (!src_line) { 210 hunk_error++; 211 break; 212 } else { 213 src_cur_line++; 214 } 215 if (strcmp(src_line, patch_line + 1) != 0) { 216 bb_error_msg("hunk #%d FAILED at %d", hunk_count, hunk_offset_start); 217 hunk_error++; 218 free(patch_line); 219 /* Probably need to find next hunk, etc... */ 220 /* but for now we just bail out */ 221 patch_line = NULL; 222 break; 223 } 224 free(src_line); 225 } 226 if (*patch_line == ' ') { 227 fputs(patch_line + 1, dst_stream); 228 dest_cur_line++; 229 } 230 } else if (*patch_line == '+') { 231 fputs(patch_line + 1, dst_stream); 232 dest_cur_line++; 233 } else { 259 plist = plist->next; 260 } 261 262 // Is this EOF? 263 if (!data) { 264 if (PATCH_DEBUG) fdprintf(2, "INEOF\n"); 265 266 // Does this hunk need to match EOF? 267 if (!plist && matcheof) break; 268 269 if (backwarn) 270 fdprintf(2,"Possibly reversed hunk %d at %ld\n", 271 TT.hunknum, TT.linenum); 272 273 // File ended before we found a place for this hunk. 274 fail_hunk(); 275 goto done; 276 } 277 278 if (PATCH_DEBUG) fdprintf(2, "IN: %s\n", data); 279 check = dlist_add(&buf, data); 280 281 // Compare this line with next expected line of hunk. 282 // todo: teach the strcmp() to ignore whitespace. 283 284 // A match can fail because the next line doesn't match, or because 285 // we hit the end of a hunk that needed EOF, and this isn't EOF. 286 287 // If match failed, flush first line of buffered data and 288 // recheck buffered data for a new match until we find one or run 289 // out of buffer. 290 291 for (;;) { 292 if (!plist || strcmp(check->data, plist->data+1)) { 293 // Match failed. Write out first line of buffered data and 294 // recheck remaining buffered data for a new match. 295 296 if (PATCH_DEBUG) 297 fdprintf(2, "NOT: %s\n", plist->data); 298 299 TT.state = 3; 300 check = buf; 301 buf = buf->next; 302 check->prev->next = buf; 303 buf->prev = check->prev; 304 do_line(check); 305 plist = TT.current_hunk; 306 307 // If we've reached the end of the buffer without confirming a 308 // match, read more lines. 309 if (check == buf) { 310 buf = NULL; 234 311 break; 235 312 } 236 free(patch_line); 313 check = buf; 314 } else { 315 if (PATCH_DEBUG) 316 fdprintf(2, "MAYBE: %s\n", plist->data); 317 // This line matches. Advance plist, detect successful match. 318 plist = plist->next; 319 if (!plist && !matcheof) goto out; 320 check = check->next; 321 if (check == buf) break; 237 322 } 238 if (hunk_error) { 239 bad_hunk_count++; 323 } 324 } 325 out: 326 // We have a match. Emit changed data. 327 TT.state = "-+"[reverse ^ dummy_revert]; 328 dlist_free(TT.current_hunk, do_line); 329 TT.current_hunk = NULL; 330 TT.state = 1; 331 done: 332 if (buf) { 333 buf->prev->next = NULL; 334 dlist_free(buf, do_line); 335 } 336 337 return TT.state; 338 } 339 340 // Read a patch file and find hunks, opening/creating/deleting files. 341 // Call apply_one_hunk() on each hunk. 342 343 // state 0: Not in a hunk, look for +++. 344 // state 1: Found +++ file indicator, look for @@ 345 // state 2: In hunk: counting initial context lines 346 // state 3: In hunk: getting body 347 348 int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 349 int patch_main(int argc UNUSED_PARAM, char **argv) 350 { 351 int opts; 352 int reverse, state = 0; 353 char *oldname = NULL, *newname = NULL; 354 char *opt_p, *opt_i; 355 356 INIT_TT(); 357 358 opts = getopt32(argv, FLAG_STR, &opt_p, &opt_i); 359 argv += optind; 360 reverse = opts & FLAG_REVERSE; 361 TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative! 362 TT.filein = TT.fileout = -1; 363 if (opts & FLAG_INPUT) { 364 xmove_fd(xopen_stdin(opt_i), STDIN_FILENO); 365 } else { 366 if (argv[0] && argv[1]) { 367 xmove_fd(xopen_stdin(argv[1]), STDIN_FILENO); 368 } 369 } 370 if (argv[0]) { 371 oldname = xstrdup(argv[0]); 372 newname = xstrdup(argv[0]); 373 } 374 375 // Loop through the lines in the patch 376 for(;;) { 377 char *patchline; 378 379 patchline = xmalloc_fgetline(stdin); 380 if (!patchline) break; 381 382 // Other versions of patch accept damaged patches, 383 // so we need to also. 384 if (!*patchline) { 385 free(patchline); 386 patchline = xstrdup(" "); 387 } 388 389 // Are we assembling a hunk? 390 if (state >= 2) { 391 if (*patchline==' ' || *patchline=='+' || *patchline=='-') { 392 dlist_add(&TT.current_hunk, patchline); 393 394 if (*patchline != '+') TT.oldlen--; 395 if (*patchline != '-') TT.newlen--; 396 397 // Context line? 398 if (*patchline==' ' && state==2) TT.context++; 399 else state=3; 400 401 // If we've consumed all expected hunk lines, apply the hunk. 402 403 if (!TT.oldlen && !TT.newlen) state = apply_one_hunk(); 404 continue; 240 405 } 241 } 242 243 /* Cleanup last patched file */ 244 if (copy_trailing_lines_flag) { 245 copy_lines(src_stream, dst_stream, -1); 246 } 247 if (src_stream) { 248 fclose(src_stream); 249 } 250 if (dst_stream) { 251 fclose(dst_stream); 252 } 253 if (bad_hunk_count) { 254 if (!ret) { 255 ret = 1; 406 fail_hunk(); 407 state = 0; 408 continue; 409 } 410 411 // Open a new file? 412 if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) { 413 char *s, **name = reverse ? &newname : &oldname; 414 int i; 415 416 if (*patchline == '+') { 417 name = reverse ? &oldname : &newname; 418 state = 1; 256 419 } 257 bb_error_msg("%d out of %d hunk FAILED", bad_hunk_count, hunk_count); 258 } else { 259 /* It worked, we can remove the backup */ 260 if (backup_filename) { 261 unlink(backup_filename); 420 421 finish_oldfile(); 422 423 if (!argv[0]) { 424 free(*name); 425 // Trim date from end of filename (if any). We don't care. 426 for (s = patchline+4; *s && *s!='\t'; s++) 427 if (*s=='\\' && s[1]) s++; 428 i = atoi(s); 429 if (i>1900 && i<=1970) 430 *name = xstrdup("/dev/null"); 431 else { 432 *s = 0; 433 *name = xstrdup(patchline+4); 434 } 262 435 } 263 if ((dest_cur_line == 0) || (dest_beg_line == 0)) { 264 /* The new patched file is empty, remove it */ 265 xunlink(new_filename); 266 if (strcmp(new_filename, original_filename) != 0) 267 xunlink(original_filename); 436 437 // We defer actually opening the file because svn produces broken 438 // patches that don't signal they want to create a new file the 439 // way the patch man page says, so you have to read the first hunk 440 // and _guess_. 441 442 // Start a new hunk? Usually @@ -oldline,oldlen +newline,newlen @@ 443 // but a missing ,value means the value is 1. 444 } else if (state == 1 && !strncmp("@@ -", patchline, 4)) { 445 int i; 446 char *s = patchline+4; 447 448 // Read oldline[,oldlen] +newline[,newlen] 449 450 TT.oldlen = TT.newlen = 1; 451 TT.oldline = strtol(s, &s, 10); 452 if (*s == ',') TT.oldlen=strtol(s+1, &s, 10); 453 TT.newline = strtol(s+2, &s, 10); 454 if (*s == ',') TT.newlen = strtol(s+1, &s, 10); 455 456 TT.context = 0; 457 state = 2; 458 459 // If this is the first hunk, open the file. 460 if (TT.filein == -1) { 461 int oldsum, newsum, empty = 0; 462 char *name; 463 464 oldsum = TT.oldline + TT.oldlen; 465 newsum = TT.newline + TT.newlen; 466 467 name = reverse ? oldname : newname; 468 469 // We're deleting oldname if new file is /dev/null (before -p) 470 // or if new hunk is empty (zero context) after patching 471 if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum)) 472 { 473 name = reverse ? newname : oldname; 474 empty++; 475 } 476 477 // handle -p path truncation. 478 for (i=0, s = name; *s;) { 479 if ((option_mask32 & FLAG_PATHLEN) && TT.prefix == i) break; 480 if (*(s++)=='/') { 481 name = s; 482 i++; 483 } 484 } 485 486 if (empty) { 487 // File is empty after the patches have been applied 488 state = 0; 489 if (option_mask32 & FLAG_RMEMPTY) { 490 // If flag -E or --remove-empty-files is set 491 printf("removing %s\n", name); 492 xunlink(name); 493 } else { 494 printf("patching file %s\n", name); 495 xclose(xopen(name, O_WRONLY | O_TRUNC)); 496 } 497 // If we've got a file to open, do so. 498 } else if (!(option_mask32 & FLAG_PATHLEN) || i <= TT.prefix) { 499 struct stat statbuf; 500 501 // If the old file was null, we're creating a new one. 502 if (!strcmp(oldname, "/dev/null") || !oldsum) { 503 printf("creating %s\n", name); 504 s = strrchr(name, '/'); 505 if (s) { 506 *s = 0; 507 bb_make_directory(name, -1, FILEUTILS_RECUR); 508 *s = '/'; 509 } 510 TT.filein = xopen(name, O_CREAT|O_EXCL|O_RDWR); 511 } else { 512 printf("patching file %s\n", name); 513 TT.filein = xopen(name, O_RDONLY); 514 } 515 516 TT.tempname = xasprintf("%sXXXXXX", name); 517 TT.fileout = xmkstemp(TT.tempname); 518 // Set permissions of output file 519 fstat(TT.filein, &statbuf); 520 fchmod(TT.fileout, statbuf.st_mode); 521 522 TT.linenum = 0; 523 TT.hunknum = 0; 524 } 268 525 } 269 } 270 } 271 272 /* 0 = SUCCESS 273 * 1 = Some hunks failed 274 * 2 = More serious problems 275 */ 276 return ret; 277 } 526 527 TT.hunknum++; 528 529 continue; 530 } 531 532 // If we didn't continue above, discard this line. 533 free(patchline); 534 } 535 536 finish_oldfile(); 537 538 if (ENABLE_FEATURE_CLEAN_UP) { 539 free(oldname); 540 free(newname); 541 } 542 543 return TT.exitval; 544 } -
branches/2.2.9/mindi-busybox/editors/sed.c
r1765 r2725 6 6 * Copyright (C) 1999,2000,2001 by Mark Whitley <markw@codepoet.org> 7 7 * Copyright (C) 2002 Matt Kraai 8 * Copyright (C) 2003 by Glenn McGrath <bug1@iinet.net.au>8 * Copyright (C) 2003 by Glenn McGrath 9 9 * Copyright (C) 2003,2004 by Rob Landley <rob@landley.net> 10 10 * 11 11 * MAINTAINER: Rob Landley <rob@landley.net> 12 12 * 13 * Licensed under GPL version 2, see file LICENSE in this tarball for details.13 * Licensed under GPLv2, see file LICENSE in this source tree. 14 14 */ 15 15 … … 24 24 (G.sed_cmd_head/G.sed_cmd_tail). 25 25 26 add_input_file() adds a FILE 26 add_input_file() adds a FILE* to the list of input files. We need to 27 27 know all input sources ahead of time to find the last line for the $ match. 28 28 … … 62 62 #include "xregex.h" 63 63 64 enum { 65 OPT_in_place = 1 << 0, 66 }; 67 64 68 /* Each sed command turns into one of these structures. */ 65 69 typedef struct sed_cmd_s { … … 77 81 char *string; /* Data string for (saicytb) commands. */ 78 82 79 unsigned short which_match;/* (s) Which match to replace (0 for all) */83 unsigned which_match; /* (s) Which match to replace (0 for all) */ 80 84 81 85 /* Bitfields (gcc won't group them if we don't) */ … … 114 118 115 119 struct pipeline { 116 char *buf; 117 int idx; 118 int len; 120 char *buf; /* Space to hold string */ 121 int idx; /* Space used */ 122 int len; /* Space allocated */ 119 123 } pipeline; 124 } FIX_ALIASING; 125 #define G (*(struct globals*)&bb_common_bufsiz1) 126 struct BUG_G_too_big { 127 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1]; 120 128 }; 121 #define G (*(struct globals*)&bb_common_bufsiz1)122 void BUG_sed_globals_too_big(void);123 129 #define INIT_G() do { \ 124 if (sizeof(struct globals) > COMMON_BUFSIZE) \125 BUG_sed_globals_too_big(); \126 130 G.sed_cmd_tail = &G.sed_cmd_head; \ 127 131 } while (0) … … 158 162 } 159 163 160 if (G.hold_space)free(G.hold_space);164 free(G.hold_space); 161 165 162 166 while (G.current_input_file < G.input_file_count) … … 174 178 } 175 179 176 /* str dup, replacing "\n" with '\n', and "\delimiter" with 'delimiter' */180 /* strcpy, replacing "\from" with 'to'. If to is NUL, replacing "\any" with 'any' */ 177 181 178 182 static void parse_escapes(char *dest, const char *string, int len, char from, char to) … … 189 193 *dest++ = string[i++]; 190 194 } 195 /* TODO: is it safe wrt a string with trailing '\\' ? */ 191 196 *dest++ = string[i++]; 192 197 } 193 *dest = 0;198 *dest = '\0'; 194 199 } 195 200 … … 199 204 200 205 parse_escapes(dest, string, len, 'n', '\n'); 206 /* GNU sed also recognizes \t */ 207 parse_escapes(dest, dest, strlen(dest), 't', '\t'); 201 208 return dest; 202 209 } … … 206 213 * index_of_next_unescaped_regexp_delim - walks left to right through a string 207 214 * beginning at a specified index and returns the index of the next regular 208 * expression delimiter (typically a forward *slash ('/')) not preceded by215 * expression delimiter (typically a forward slash ('/')) not preceded by 209 216 * a backslash ('\'). A negative delimiter disables square bracket checking. 210 217 */ … … 299 306 300 307 /* Grab a filename. Whitespace at start is skipped, then goes to EOL. */ 301 static int parse_file_cmd( sed_cmd_t *sed_cmd,const char *filecmdstr, char **retval)308 static int parse_file_cmd(/*sed_cmd_t *sed_cmd,*/ const char *filecmdstr, char **retval) 302 309 { 303 310 int start = 0, idx, hack = 0; … … 351 358 const char *pos = substr + idx; 352 359 /* FIXME: error check? */ 353 sed_cmd->which_match = (unsigned short)strtol(substr+idx, (char**) &pos, 10);360 sed_cmd->which_match = (unsigned)strtol(substr+idx, (char**) &pos, 10); 354 361 idx = pos - substr; 355 362 } … … 357 364 } 358 365 /* Skip spaces */ 359 if (isspace(substr[idx])) continue; 366 if (isspace(substr[idx])) 367 continue; 360 368 361 369 switch (substr[idx]) { 362 370 /* Replace all occurrences */ 363 371 case 'g': 364 if (match[0] != '^') sed_cmd->which_match = 0; 372 if (match[0] != '^') 373 sed_cmd->which_match = 0; 365 374 break; 366 375 /* Print pattern space */ … … 372 381 { 373 382 char *temp; 374 idx += parse_file_cmd( sed_cmd,substr+idx, &temp);383 idx += parse_file_cmd(/*sed_cmd,*/ substr+idx, &temp); 375 384 break; 376 385 } … … 381 390 /* Comment */ 382 391 case '#': 383 while (substr[++idx]) /*skip all*/; 392 // while (substr[++idx]) continue; 393 idx += strlen(substr + idx); // same 384 394 /* Fall through */ 385 395 /* End of command */ … … 391 401 } 392 402 } 393 out:403 out: 394 404 /* compile the match string into a regex */ 395 405 if (*match != '\0') { … … 414 424 else if (strchr("aic", sed_cmd->cmd)) { 415 425 if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c') 416 bb_error_msg_and_die 417 ("only a beginning address can be specified for edit commands"); 426 bb_error_msg_and_die("only a beginning address can be specified for edit commands"); 418 427 for (;;) { 419 428 if (*cmdstr == '\n' || *cmdstr == '\\') { 420 429 cmdstr++; 421 430 break; 422 } else if (isspace(*cmdstr)) 423 cmdstr++; 424 else 431 } 432 if (!isspace(*cmdstr)) 425 433 break; 434 cmdstr++; 426 435 } 427 436 sed_cmd->string = xstrdup(cmdstr); 428 parse_escapes(sed_cmd->string, sed_cmd->string, strlen(cmdstr), 0, 0); 437 /* "\anychar" -> "anychar" */ 438 parse_escapes(sed_cmd->string, sed_cmd->string, strlen(cmdstr), '\0', '\0'); 429 439 cmdstr += strlen(cmdstr); 430 440 /* handle file cmds: (r)ead */ … … 432 442 if (sed_cmd->end_line || sed_cmd->end_match) 433 443 bb_error_msg_and_die("command only uses one address"); 434 cmdstr += parse_file_cmd( sed_cmd,cmdstr, &sed_cmd->string);444 cmdstr += parse_file_cmd(/*sed_cmd,*/ cmdstr, &sed_cmd->string); 435 445 if (sed_cmd->cmd == 'w') { 436 sed_cmd->sw_file = xfopen (sed_cmd->string, "w");446 sed_cmd->sw_file = xfopen_for_write(sed_cmd->string); 437 447 sed_cmd->sw_last_char = '\n'; 438 448 } … … 483 493 { 484 494 sed_cmd_t *sed_cmd; 485 int temp;495 unsigned len, n; 486 496 487 497 /* Append this line to any unfinished line from last time. */ … … 492 502 } 493 503 494 /* If this line ends with backslash, request next line. */ 495 temp = strlen(cmdstr); 496 if (temp && cmdstr[--temp] == '\\') { 504 /* If this line ends with unescaped backslash, request next line. */ 505 n = len = strlen(cmdstr); 506 while (n && cmdstr[n-1] == '\\') 507 n--; 508 if ((len - n) & 1) { /* if odd number of trailing backslashes */ 497 509 if (!G.add_cmd_line) 498 510 G.add_cmd_line = xstrdup(cmdstr); 499 G.add_cmd_line[ temp] = '\0';511 G.add_cmd_line[len-1] = '\0'; 500 512 return; 501 513 } … … 556 568 if (!*cmdstr) 557 569 bb_error_msg_and_die("missing command"); 558 sed_cmd->cmd = * (cmdstr++);570 sed_cmd->cmd = *cmdstr++; 559 571 cmdstr = parse_cmd_args(sed_cmd, cmdstr); 560 572 … … 585 597 static void do_subst_w_backrefs(char *line, char *replace) 586 598 { 587 int i, j;599 int i, j; 588 600 589 601 /* go through the replacement string */ … … 620 632 } 621 633 622 static int do_subst_command(sed_cmd_t *sed_cmd, char **line )623 { 624 char * oldline = *line;634 static int do_subst_command(sed_cmd_t *sed_cmd, char **line_p) 635 { 636 char *line = *line_p; 625 637 int altered = 0; 626 intmatch_count = 0;638 unsigned match_count = 0; 627 639 regex_t *current_regex; 628 640 641 current_regex = sed_cmd->sub_match; 629 642 /* Handle empty regex. */ 630 if ( sed_cmd->sub_match == NULL) {643 if (!current_regex) { 631 644 current_regex = G.previous_regex_ptr; 632 645 if (!current_regex) 633 646 bb_error_msg_and_die("no previous regexp"); 634 } else635 G.previous_regex_ptr = current_regex = sed_cmd->sub_match;647 } 648 G.previous_regex_ptr = current_regex; 636 649 637 650 /* Find the first match */ 638 if (REG_NOMATCH == regexec(current_regex, oldline, 10, G.regmatch, 0))651 if (REG_NOMATCH == regexec(current_regex, line, 10, G.regmatch, 0)) 639 652 return 0; 640 653 … … 653 666 echo "hi" | busybox sed 's/^/!/g' */ 654 667 if (!G.regmatch[0].rm_so && !G.regmatch[0].rm_eo && match_count) { 655 pipe_putc(* oldline++);668 pipe_putc(*line++); 656 669 continue; 657 670 } … … 661 674 /* If we aren't interested in this match, output old line to 662 675 end of match and continue */ 663 if (sed_cmd->which_match && sed_cmd->which_match != match_count) { 676 if (sed_cmd->which_match 677 && (sed_cmd->which_match != match_count) 678 ) { 664 679 for (i = 0; i < G.regmatch[0].rm_eo; i++) 665 pipe_putc(* oldline++);680 pipe_putc(*line++); 666 681 continue; 667 682 } … … 669 684 /* print everything before the match */ 670 685 for (i = 0; i < G.regmatch[0].rm_so; i++) 671 pipe_putc( oldline[i]);686 pipe_putc(line[i]); 672 687 673 688 /* then print the substitution string */ 674 do_subst_w_backrefs( oldline, sed_cmd->string);689 do_subst_w_backrefs(line, sed_cmd->string); 675 690 676 691 /* advance past the match */ 677 oldline += G.regmatch[0].rm_eo;692 line += G.regmatch[0].rm_eo; 678 693 /* flag that something has changed */ 679 694 altered++; 680 695 681 696 /* if we're not doing this globally, get out now */ 682 if (sed_cmd->which_match) break; 683 } while (*oldline && (regexec(current_regex, oldline, 10, G.regmatch, 0) != REG_NOMATCH)); 697 if (sed_cmd->which_match) 698 break; 699 700 //maybe (G.regmatch[0].rm_eo ? REG_NOTBOL : 0) instead of unconditional REG_NOTBOL? 701 } while (*line && regexec(current_regex, line, 10, G.regmatch, REG_NOTBOL) != REG_NOMATCH); 684 702 685 703 /* Copy rest of string into output pipeline */ 686 687 while (*oldline) 688 pipe_putc(*oldline++); 689 pipe_putc(0); 690 691 free(*line); 692 *line = G.pipeline.buf; 704 while (1) { 705 char c = *line++; 706 pipe_putc(c); 707 if (c == '\0') 708 break; 709 } 710 711 free(*line_p); 712 *line_p = G.pipeline.buf; 693 713 return altered; 694 714 } … … 725 745 static void add_input_file(FILE *file) 726 746 { 727 G.input_file_list = xrealloc(G.input_file_list, 728 (G.input_file_count + 1) * sizeof(FILE *)); 747 G.input_file_list = xrealloc_vector(G.input_file_list, 2, G.input_file_count); 729 748 G.input_file_list[G.input_file_count++] = file; 730 749 } … … 859 878 next_line = get_next_line(&next_gets_char); 860 879 861 /* go through every line in each file */862 again:880 /* Go through every line in each file */ 881 again: 863 882 substituted = 0; 864 883 865 884 /* Advance to next line. Stop if out of lines. */ 866 885 pattern_space = next_line; 867 if (!pattern_space) return; 886 if (!pattern_space) 887 return; 868 888 last_gets_char = next_gets_char; 869 889 … … 872 892 next_line = get_next_line(&next_gets_char); 873 893 linenum++; 874 restart: 875 /* for every line, go through all the commands */ 894 895 /* For every line, go through all the commands */ 896 restart: 876 897 for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) { 877 898 int old_matched, matched; … … 880 901 881 902 /* Determine if this command matches this line: */ 903 904 //bb_error_msg("match1:%d", sed_cmd->in_match); 905 //bb_error_msg("match2:%d", (!sed_cmd->beg_line && !sed_cmd->end_line 906 // && !sed_cmd->beg_match && !sed_cmd->end_match)); 907 //bb_error_msg("match3:%d", (sed_cmd->beg_line > 0 908 // && (sed_cmd->end_line || sed_cmd->end_match 909 // ? (sed_cmd->beg_line <= linenum) 910 // : (sed_cmd->beg_line == linenum) 911 // ) 912 // ) 913 //bb_error_msg("match4:%d", (beg_match(sed_cmd, pattern_space))); 914 //bb_error_msg("match5:%d", (sed_cmd->beg_line == -1 && next_line == NULL)); 882 915 883 916 /* Are we continuing a previous multi-line match? */ … … 887 920 && !sed_cmd->beg_match && !sed_cmd->end_match) 888 921 /* Or did we match the start of a numerical range? */ 889 || (sed_cmd->beg_line > 0 && (sed_cmd->beg_line == linenum)) 922 || (sed_cmd->beg_line > 0 923 && (sed_cmd->end_line || sed_cmd->end_match 924 /* note: even if end is numeric and is < linenum too, 925 * GNU sed matches! We match too */ 926 ? (sed_cmd->beg_line <= linenum) /* N,end */ 927 : (sed_cmd->beg_line == linenum) /* N */ 928 ) 929 ) 890 930 /* Or does this line match our begin address regex? */ 891 931 || (beg_match(sed_cmd, pattern_space)) … … 894 934 895 935 /* Snapshot the value */ 896 897 936 matched = sed_cmd->in_match; 898 937 938 //bb_error_msg("cmd:'%c' matched:%d beg_line:%d end_line:%d linenum:%d", 939 //sed_cmd->cmd, matched, sed_cmd->beg_line, sed_cmd->end_line, linenum); 940 899 941 /* Is this line the end of the current match? */ 900 942 901 943 if (matched) { 944 /* once matched, "n,xxx" range is dead, disabling it */ 945 if (sed_cmd->beg_line > 0 946 && !(option_mask32 & OPT_in_place) /* but not for -i */ 947 ) { 948 sed_cmd->beg_line = -2; 949 } 902 950 sed_cmd->in_match = !( 903 951 /* has the ending line come, or is this a single address command? */ … … 915 963 } 916 964 917 /* Skip blocks of commands we didn't match .*/965 /* Skip blocks of commands we didn't match */ 918 966 if (sed_cmd->cmd == '{') { 919 967 if (sed_cmd->invert ? matched : !matched) { 920 while (sed_cmd->cmd != '}') { 968 unsigned nest_cnt = 0; 969 while (1) { 970 if (sed_cmd->cmd == '{') 971 nest_cnt++; 972 if (sed_cmd->cmd == '}') { 973 nest_cnt--; 974 if (nest_cnt == 0) 975 break; 976 } 921 977 sed_cmd = sed_cmd->next; 922 978 if (!sed_cmd) … … 928 984 929 985 /* Okay, so did this line match? */ 930 if (sed_cmd->invert ? !matched : matched) { 931 /* Update last used regex in case a blank substitute BRE is found */ 932 if (sed_cmd->beg_match) { 933 G.previous_regex_ptr = sed_cmd->beg_match; 934 } 935 936 /* actual sedding */ 937 switch (sed_cmd->cmd) { 938 939 /* Print line number */ 940 case '=': 941 fprintf(G.nonstdout, "%d\n", linenum); 986 if (sed_cmd->invert ? matched : !matched) 987 continue; /* no */ 988 989 /* Update last used regex in case a blank substitute BRE is found */ 990 if (sed_cmd->beg_match) { 991 G.previous_regex_ptr = sed_cmd->beg_match; 992 } 993 994 /* actual sedding */ 995 //bb_error_msg("pattern_space:'%s' next_line:'%s' cmd:%c", 996 //pattern_space, next_line, sed_cmd->cmd); 997 switch (sed_cmd->cmd) { 998 999 /* Print line number */ 1000 case '=': 1001 fprintf(G.nonstdout, "%d\n", linenum); 1002 break; 1003 1004 /* Write the current pattern space up to the first newline */ 1005 case 'P': 1006 { 1007 char *tmp = strchr(pattern_space, '\n'); 1008 if (tmp) { 1009 *tmp = '\0'; 1010 /* TODO: explain why '\n' below */ 1011 sed_puts(pattern_space, '\n'); 1012 *tmp = '\n'; 942 1013 break; 943 944 /* Write the current pattern space up to the first newline */ 945 case 'P': 946 { 947 char *tmp = strchr(pattern_space, '\n'); 948 949 if (tmp) { 950 *tmp = '\0'; 951 /* TODO: explain why '\n' below */ 952 sed_puts(pattern_space, '\n'); 953 *tmp = '\n'; 954 break; 955 } 956 /* Fall Through */ 957 } 958 959 /* Write the current pattern space to output */ 960 case 'p': 961 /* NB: we print this _before_ the last line 962 * (of current file) is printed. Even if 963 * that line is nonterminated, we print 964 * '\n' here (gnu sed does the same) */ 965 sed_puts(pattern_space, '\n'); 1014 } 1015 /* Fall Through */ 1016 } 1017 1018 /* Write the current pattern space to output */ 1019 case 'p': 1020 /* NB: we print this _before_ the last line 1021 * (of current file) is printed. Even if 1022 * that line is nonterminated, we print 1023 * '\n' here (gnu sed does the same) */ 1024 sed_puts(pattern_space, '\n'); 1025 break; 1026 /* Delete up through first newline */ 1027 case 'D': 1028 { 1029 char *tmp = strchr(pattern_space, '\n'); 1030 if (tmp) { 1031 overlapping_strcpy(pattern_space, tmp + 1); 1032 goto restart; 1033 } 1034 } 1035 /* discard this line. */ 1036 case 'd': 1037 goto discard_line; 1038 1039 /* Substitute with regex */ 1040 case 's': 1041 if (!do_subst_command(sed_cmd, &pattern_space)) 966 1042 break; 967 /* Delete up through first newline */ 968 case 'D': 969 { 970 char *tmp = strchr(pattern_space, '\n'); 971 972 if (tmp) { 973 tmp = xstrdup(tmp+1); 974 free(pattern_space); 975 pattern_space = tmp; 976 goto restart; 977 } 978 } 979 /* discard this line. */ 980 case 'd': 981 goto discard_line; 982 983 /* Substitute with regex */ 984 case 's': 985 if (!do_subst_command(sed_cmd, &pattern_space)) 986 break; 987 substituted |= 1; 988 989 /* handle p option */ 990 if (sed_cmd->sub_p) 991 sed_puts(pattern_space, last_gets_char); 992 /* handle w option */ 993 if (sed_cmd->sw_file) 994 puts_maybe_newline( 995 pattern_space, sed_cmd->sw_file, 996 &sed_cmd->sw_last_char, last_gets_char); 997 break; 998 999 /* Append line to linked list to be printed later */ 1000 case 'a': 1001 append(sed_cmd->string); 1002 break; 1003 1004 /* Insert text before this line */ 1005 case 'i': 1006 sed_puts(sed_cmd->string, '\n'); 1007 break; 1008 1009 /* Cut and paste text (replace) */ 1010 case 'c': 1011 /* Only triggers on last line of a matching range. */ 1012 if (!sed_cmd->in_match) 1013 sed_puts(sed_cmd->string, NO_EOL_CHAR); 1014 goto discard_line; 1015 1016 /* Read file, append contents to output */ 1017 case 'r': 1018 { 1019 FILE *rfile; 1020 1021 rfile = fopen(sed_cmd->string, "r"); 1022 if (rfile) { 1023 char *line; 1024 1025 while ((line = xmalloc_getline(rfile)) 1026 != NULL) 1027 append(line); 1028 xprint_and_close_file(rfile); 1029 } 1030 1031 break; 1032 } 1033 1034 /* Write pattern space to file. */ 1035 case 'w': 1043 substituted |= 1; 1044 1045 /* handle p option */ 1046 if (sed_cmd->sub_p) 1047 sed_puts(pattern_space, last_gets_char); 1048 /* handle w option */ 1049 if (sed_cmd->sw_file) 1036 1050 puts_maybe_newline( 1037 1051 pattern_space, sed_cmd->sw_file, 1038 1052 &sed_cmd->sw_last_char, last_gets_char); 1039 break; 1040 1041 /* Read next line from input */ 1042 case 'n': 1043 if (!G.be_quiet) 1044 sed_puts(pattern_space, last_gets_char); 1045 if (next_line) { 1046 free(pattern_space); 1047 pattern_space = next_line; 1048 last_gets_char = next_gets_char; 1049 next_line = get_next_line(&next_gets_char); 1050 linenum++; 1051 break; 1052 } 1053 /* fall through */ 1054 1055 /* Quit. End of script, end of input. */ 1056 case 'q': 1057 /* Exit the outer while loop */ 1058 free(next_line); 1059 next_line = NULL; 1060 goto discard_commands; 1061 1062 /* Append the next line to the current line */ 1063 case 'N': 1064 { 1065 int len; 1066 /* If no next line, jump to end of script and exit. */ 1067 if (next_line == NULL) { 1068 /* Jump to end of script and exit */ 1069 free(next_line); 1070 next_line = NULL; 1071 goto discard_line; 1072 /* append next_line, read new next_line. */ 1073 } 1074 len = strlen(pattern_space); 1075 pattern_space = realloc(pattern_space, len + strlen(next_line) + 2); 1076 pattern_space[len] = '\n'; 1077 strcpy(pattern_space + len+1, next_line); 1053 break; 1054 1055 /* Append line to linked list to be printed later */ 1056 case 'a': 1057 append(sed_cmd->string); 1058 break; 1059 1060 /* Insert text before this line */ 1061 case 'i': 1062 sed_puts(sed_cmd->string, '\n'); 1063 break; 1064 1065 /* Cut and paste text (replace) */ 1066 case 'c': 1067 /* Only triggers on last line of a matching range. */ 1068 if (!sed_cmd->in_match) 1069 sed_puts(sed_cmd->string, '\n'); 1070 goto discard_line; 1071 1072 /* Read file, append contents to output */ 1073 case 'r': 1074 { 1075 FILE *rfile; 1076 rfile = fopen_for_read(sed_cmd->string); 1077 if (rfile) { 1078 char *line; 1079 1080 while ((line = xmalloc_fgetline(rfile)) 1081 != NULL) 1082 append(line); 1083 xprint_and_close_file(rfile); 1084 } 1085 1086 break; 1087 } 1088 1089 /* Write pattern space to file. */ 1090 case 'w': 1091 puts_maybe_newline( 1092 pattern_space, sed_cmd->sw_file, 1093 &sed_cmd->sw_last_char, last_gets_char); 1094 break; 1095 1096 /* Read next line from input */ 1097 case 'n': 1098 if (!G.be_quiet) 1099 sed_puts(pattern_space, last_gets_char); 1100 if (next_line) { 1101 free(pattern_space); 1102 pattern_space = next_line; 1078 1103 last_gets_char = next_gets_char; 1079 1104 next_line = get_next_line(&next_gets_char); 1105 substituted = 0; 1080 1106 linenum++; 1081 1107 break; 1082 1108 } 1083 1084 /* Test/branch if substitution occurred */ 1085 case 't': 1086 if (!substituted) break; 1087 substituted = 0; 1088 /* Fall through */ 1089 /* Test/branch if substitution didn't occur */ 1090 case 'T': 1091 if (substituted) break; 1092 /* Fall through */ 1093 /* Branch to label */ 1094 case 'b': 1095 if (!sed_cmd->string) goto discard_commands; 1096 else sed_cmd = branch_to(sed_cmd->string); 1097 break; 1098 /* Transliterate characters */ 1099 case 'y': 1100 { 1101 int i, j; 1102 1103 for (i = 0; pattern_space[i]; i++) { 1104 for (j = 0; sed_cmd->string[j]; j += 2) { 1105 if (pattern_space[i] == sed_cmd->string[j]) { 1106 pattern_space[i] = sed_cmd->string[j + 1]; 1107 break; 1108 } 1109 /* fall through */ 1110 1111 /* Quit. End of script, end of input. */ 1112 case 'q': 1113 /* Exit the outer while loop */ 1114 free(next_line); 1115 next_line = NULL; 1116 goto discard_commands; 1117 1118 /* Append the next line to the current line */ 1119 case 'N': 1120 { 1121 int len; 1122 /* If no next line, jump to end of script and exit. */ 1123 /* http://www.gnu.org/software/sed/manual/sed.html: 1124 * "Most versions of sed exit without printing anything 1125 * when the N command is issued on the last line of 1126 * a file. GNU sed prints pattern space before exiting 1127 * unless of course the -n command switch has been 1128 * specified. This choice is by design." 1129 */ 1130 if (next_line == NULL) { 1131 //goto discard_line; 1132 goto discard_commands; /* GNU behavior */ 1133 } 1134 /* Append next_line, read new next_line. */ 1135 len = strlen(pattern_space); 1136 pattern_space = xrealloc(pattern_space, len + strlen(next_line) + 2); 1137 pattern_space[len] = '\n'; 1138 strcpy(pattern_space + len+1, next_line); 1139 last_gets_char = next_gets_char; 1140 next_line = get_next_line(&next_gets_char); 1141 linenum++; 1142 break; 1143 } 1144 1145 /* Test/branch if substitution occurred */ 1146 case 't': 1147 if (!substituted) break; 1148 substituted = 0; 1149 /* Fall through */ 1150 /* Test/branch if substitution didn't occur */ 1151 case 'T': 1152 if (substituted) break; 1153 /* Fall through */ 1154 /* Branch to label */ 1155 case 'b': 1156 if (!sed_cmd->string) goto discard_commands; 1157 else sed_cmd = branch_to(sed_cmd->string); 1158 break; 1159 /* Transliterate characters */ 1160 case 'y': 1161 { 1162 int i, j; 1163 for (i = 0; pattern_space[i]; i++) { 1164 for (j = 0; sed_cmd->string[j]; j += 2) { 1165 if (pattern_space[i] == sed_cmd->string[j]) { 1166 pattern_space[i] = sed_cmd->string[j + 1]; 1167 break; 1109 1168 } 1110 1169 } 1111 1112 break; 1113 }1114 case 'g': /* Replace pattern space with hold space */1115 free(pattern_space);1116 pattern_space = xstrdup(G.hold_space ? G.hold_space : "");1117 break;1118 case 'G': /* Append newline and hold space to pattern space */1119 {1120 int pattern_space_size = 2;1121 int hold_space_size = 0;1122 1123 if (pattern_space) 1124 pattern_space_size += strlen(pattern_space);1125 if (G.hold_space)1126 hold_space_size = strlen(G.hold_space);1127 pattern_space = xrealloc(pattern_space,1128 pattern_space_size + hold_space_size);1129 if (pattern_space_size == 2)1130 pattern_space[0] = 0;1131 strcat(pattern_space, "\n");1132 if (G.hold_space)1133 strcat(pattern_space, G.hold_space);1134 last_gets_char = '\n';1135 1136 break; 1137 }1138 case 'h': /* Replace hold space with pattern space */1139 free(G.hold_space);1140 G.hold_space = xstrdup(pattern_space);1141 break;1142 case 'H': /* Append newline and pattern space to hold space */1143 {1144 int hold_space_size = 2;1145 int pattern_space_size = 0;1146 1147 if (G.hold_space) 1148 hold_space_size += strlen(G.hold_space);1149 if (pattern_space)1150 pattern_space_size = strlen(pattern_space);1151 G.hold_space = xrealloc(G.hold_space,1152 hold_space_size + pattern_space_size);1153 1154 if (hold_space_size == 2) 1155 *G.hold_space = 0;1156 strcat(G.hold_space, "\n");1157 if (pattern_space)1158 strcat(G.hold_space, pattern_space);1159 1160 break; 1161 }1162 case 'x': /* Exchange hold and pattern space */1163 {1164 char *tmp = pattern_space;1165 pattern_space = G.hold_space ? : xzalloc(1);1166 last_gets_char = '\n';1167 G.hold_space = tmp;1168 break;1169 }1170 1171 } 1172 } 1170 } 1171 1172 break; 1173 } 1174 case 'g': /* Replace pattern space with hold space */ 1175 free(pattern_space); 1176 pattern_space = xstrdup(G.hold_space ? G.hold_space : ""); 1177 break; 1178 case 'G': /* Append newline and hold space to pattern space */ 1179 { 1180 int pattern_space_size = 2; 1181 int hold_space_size = 0; 1182 1183 if (pattern_space) 1184 pattern_space_size += strlen(pattern_space); 1185 if (G.hold_space) 1186 hold_space_size = strlen(G.hold_space); 1187 pattern_space = xrealloc(pattern_space, 1188 pattern_space_size + hold_space_size); 1189 if (pattern_space_size == 2) 1190 pattern_space[0] = 0; 1191 strcat(pattern_space, "\n"); 1192 if (G.hold_space) 1193 strcat(pattern_space, G.hold_space); 1194 last_gets_char = '\n'; 1195 1196 break; 1197 } 1198 case 'h': /* Replace hold space with pattern space */ 1199 free(G.hold_space); 1200 G.hold_space = xstrdup(pattern_space); 1201 break; 1202 case 'H': /* Append newline and pattern space to hold space */ 1203 { 1204 int hold_space_size = 2; 1205 int pattern_space_size = 0; 1206 1207 if (G.hold_space) 1208 hold_space_size += strlen(G.hold_space); 1209 if (pattern_space) 1210 pattern_space_size = strlen(pattern_space); 1211 G.hold_space = xrealloc(G.hold_space, 1212 hold_space_size + pattern_space_size); 1213 1214 if (hold_space_size == 2) 1215 *G.hold_space = 0; 1216 strcat(G.hold_space, "\n"); 1217 if (pattern_space) 1218 strcat(G.hold_space, pattern_space); 1219 1220 break; 1221 } 1222 case 'x': /* Exchange hold and pattern space */ 1223 { 1224 char *tmp = pattern_space; 1225 pattern_space = G.hold_space ? G.hold_space : xzalloc(1); 1226 last_gets_char = '\n'; 1227 G.hold_space = tmp; 1228 break; 1229 } 1230 } /* switch */ 1231 } /* for each cmd */ 1173 1232 1174 1233 /* 1175 * exit point from sedding...1234 * Exit point from sedding... 1176 1235 */ 1177 1236 discard_commands: … … 1212 1271 /* Odd number of preceding slashes - newline is escaped */ 1213 1272 if (slashes & 1) { 1214 strcpy(eol-1, eol);1273 overlapping_strcpy(eol - 1, eol); 1215 1274 eol = strchr(eol, '\n'); 1216 1275 goto next; … … 1224 1283 } 1225 1284 1226 int sed_main(int argc, char **argv); 1227 int sed_main(int argc, char **argv) 1228 { 1229 enum { 1230 OPT_in_place = 1 << 0, 1231 }; 1285 int sed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1286 int sed_main(int argc UNUSED_PARAM, char **argv) 1287 { 1232 1288 unsigned opt; 1233 1289 llist_t *opt_e, *opt_f; … … 1240 1296 1241 1297 /* Lie to autoconf when it starts asking stupid questions. */ 1242 if (arg c == 2&& !strcmp(argv[1], "--version")) {1298 if (argv[1] && !strcmp(argv[1], "--version")) { 1243 1299 puts("This is not GNU sed version 4.0"); 1244 1300 return 0; … … 1249 1305 opt_complementary = "e::f::" /* can occur multiple times */ 1250 1306 "nn"; /* count -n */ 1307 /* -i must be first, to match OPT_in_place definition */ 1251 1308 opt = getopt32(argv, "irne:f:", &opt_e, &opt_f, 1252 1309 &G.be_quiet); /* counter for -n */ 1253 argc -= optind;1310 //argc -= optind; 1254 1311 argv += optind; 1255 1312 if (opt & OPT_in_place) { // -i … … 1259 1316 //if (opt & 0x4) G.be_quiet++; // -n 1260 1317 while (opt_e) { // -e 1261 add_cmd_block(opt_e->data); 1262 opt_e = opt_e->link; 1263 /* we leak opt_e here... */ 1318 add_cmd_block(llist_pop(&opt_e)); 1264 1319 } 1265 1320 while (opt_f) { // -f 1266 1321 char *line; 1267 1322 FILE *cmdfile; 1268 cmdfile = xfopen (opt_f->data, "r");1269 while ((line = xmalloc_ getline(cmdfile)) != NULL) {1323 cmdfile = xfopen_for_read(llist_pop(&opt_f)); 1324 while ((line = xmalloc_fgetline(cmdfile)) != NULL) { 1270 1325 add_cmd(line); 1271 1326 free(line); 1272 1327 } 1273 1328 fclose(cmdfile); 1274 opt_f = opt_f->link;1275 /* we leak opt_f here... */1276 1329 } 1277 1330 /* if we didn't get a pattern from -e or -f, use argv[0] */ 1278 1331 if (!(opt & 0x18)) { 1279 if (! argc)1332 if (!*argv) 1280 1333 bb_show_usage(); 1281 1334 add_cmd_block(*argv++); 1282 argc--;1283 1335 } 1284 1336 /* Flush any unfinished commands. */ … … 1295 1347 bb_error_msg_and_die(bb_msg_requires_arg, "-i"); 1296 1348 add_input_file(stdin); 1297 process_files();1298 1349 } else { 1299 1350 int i; 1300 1351 FILE *file; 1301 1352 1302 for (i = 0; i < argc; i++) {1353 for (i = 0; argv[i]; i++) { 1303 1354 struct stat statbuf; 1304 1355 int nonstdoutfd; … … 1320 1371 1321 1372 G.outname = xasprintf("%sXXXXXX", argv[i]); 1322 nonstdoutfd = mkstemp(G.outname); 1323 if (-1 == nonstdoutfd) 1324 bb_perror_msg_and_die("cannot create temp file %s", G.outname); 1325 G.nonstdout = fdopen(nonstdoutfd, "w"); 1326 1327 /* Set permissions of output file */ 1328 1373 nonstdoutfd = xmkstemp(G.outname); 1374 G.nonstdout = xfdopen_for_write(nonstdoutfd); 1375 1376 /* Set permissions/owner of output file */ 1329 1377 fstat(fileno(file), &statbuf); 1378 /* chmod'ing AFTER chown would preserve suid/sgid bits, 1379 * but GNU sed 4.2.1 does not preserve them either */ 1330 1380 fchmod(nonstdoutfd, statbuf.st_mode); 1381 fchown(nonstdoutfd, statbuf.st_uid, statbuf.st_gid); 1331 1382 add_input_file(file); 1332 1383 process_files(); … … 1335 1386 G.nonstdout = stdout; 1336 1387 /* unlink(argv[i]); */ 1337 // FIXME: error check / message? 1338 rename(G.outname, argv[i]); 1388 xrename(G.outname, argv[i]); 1339 1389 free(G.outname); 1340 G.outname = 0; 1341 } 1342 if (G.input_file_count > G.current_input_file) 1343 process_files(); 1344 } 1390 G.outname = NULL; 1391 } 1392 /* Here, to handle "sed 'cmds' nonexistent_file" case we did: 1393 * if (G.current_input_file >= G.input_file_count) 1394 * return status; 1395 * but it's not needed since process_files() works correctly 1396 * in this case too. */ 1397 } 1398 process_files(); 1345 1399 1346 1400 return status; -
branches/2.2.9/mindi-busybox/editors/vi.c
r1765 r2725 4 4 * Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.com> 5 5 * 6 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.6 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 7 7 */ 8 8 … … 24 24 #include "libbb.h" 25 25 26 /* the CRASHME code is unmaintained, and doesn't currently build */ 26 27 #define ENABLE_FEATURE_VI_CRASHME 0 27 28 29 28 30 #if ENABLE_LOCALE_SUPPORT 29 #define Isprint(c) isprint((c)) 31 32 #if ENABLE_FEATURE_VI_8BIT 33 //FIXME: this does not work properly for Unicode anyway 34 # define Isprint(c) (isprint)(c) 30 35 #else 36 # define Isprint(c) isprint_asciionly(c) 37 #endif 38 39 #else 40 31 41 /* 0x9b is Meta-ESC */ 42 #if ENABLE_FEATURE_VI_8BIT 32 43 #define Isprint(c) ((unsigned char)(c) >= ' ' && (c) != 0x7f && (unsigned char)(c) != 0x9b) 33 #endif 44 #else 45 #define Isprint(c) ((unsigned char)(c) >= ' ' && (unsigned char)(c) < 0x7f) 46 #endif 47 48 #endif 49 34 50 35 51 enum { 36 MAX_LINELEN = CONFIG_FEATURE_VI_MAX_LEN, 52 MAX_TABSTOP = 32, // sanity limit 53 // User input len. Need not be extra big. 54 // Lines in file being edited *can* be bigger than this. 55 MAX_INPUT_LEN = 128, 56 // Sanity limits. We have only one buffer of this size. 37 57 MAX_SCR_COLS = CONFIG_FEATURE_VI_MAX_LEN, 58 MAX_SCR_ROWS = CONFIG_FEATURE_VI_MAX_LEN, 38 59 }; 39 40 // Misc. non-Ascii keys that report an escape sequence41 #define VI_K_UP (char)128 // cursor key Up42 #define VI_K_DOWN (char)129 // cursor key Down43 #define VI_K_RIGHT (char)130 // Cursor Key Right44 #define VI_K_LEFT (char)131 // cursor key Left45 #define VI_K_HOME (char)132 // Cursor Key Home46 #define VI_K_END (char)133 // Cursor Key End47 #define VI_K_INSERT (char)134 // Cursor Key Insert48 #define VI_K_PAGEUP (char)135 // Cursor Key Page Up49 #define VI_K_PAGEDOWN (char)136 // Cursor Key Page Down50 #define VI_K_FUN1 (char)137 // Function Key F151 #define VI_K_FUN2 (char)138 // Function Key F252 #define VI_K_FUN3 (char)139 // Function Key F353 #define VI_K_FUN4 (char)140 // Function Key F454 #define VI_K_FUN5 (char)141 // Function Key F555 #define VI_K_FUN6 (char)142 // Function Key F656 #define VI_K_FUN7 (char)143 // Function Key F757 #define VI_K_FUN8 (char)144 // Function Key F858 #define VI_K_FUN9 (char)145 // Function Key F959 #define VI_K_FUN10 (char)146 // Function Key F1060 #define VI_K_FUN11 (char)147 // Function Key F1161 #define VI_K_FUN12 (char)148 // Function Key F1262 60 63 61 /* vt102 typical ESC sequence */ 64 62 /* terminal standout start/normal ESC sequence */ 65 static const char SOs[] ALIGN1 = "\033[7m"; 66 static const char SOn[] ALIGN1 = "\033[0m"; 63 #define SOs "\033[7m" 64 #define SOn "\033[0m" 67 65 /* terminal bell sequence */ 68 static const char bell[] ALIGN1 = "\007"; 66 #define bell "\007" 69 67 /* Clear-end-of-line and Clear-end-of-screen ESC sequence */ 70 static const char Ceol[] ALIGN1 = "\033[0K"; 71 static const char Ceos[] ALIGN1 = "\033[0J"; 68 #define Ceol "\033[K" 69 #define Ceos "\033[J" 72 70 /* Cursor motion arbitrary destination ESC sequence */ 73 static const char CMrc[] ALIGN1 = "\033[%d;%dH"; 71 #define CMrc "\033[%u;%uH" 74 72 /* Cursor motion up and down ESC sequence */ 75 static const char CMup[] ALIGN1 = "\033[A"; 76 static const char CMdown[] ALIGN1 = "\n"; 77 73 #define CMup "\033[A" 74 #define CMdown "\n" 75 76 #if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK 77 // cmds modifying text[] 78 // vda: removed "aAiIs" as they switch us into insert mode 79 // and remembering input for replay after them makes no sense 80 static const char modifying_cmds[] = "cCdDJoOpPrRxX<>~"; 81 #endif 78 82 79 83 enum { … … 92 96 }; 93 97 98 94 99 /* vi.c expects chars to be unsigned. */ 95 100 /* busybox build system provides that, but it's better */ 96 101 /* to audit and fix the source */ 97 102 98 static smallint vi_setops; 103 struct globals { 104 /* many references - keep near the top of globals */ 105 char *text, *end; // pointers to the user data in memory 106 char *dot; // where all the action takes place 107 int text_size; // size of the allocated buffer 108 109 /* the rest */ 110 smallint vi_setops; 99 111 #define VI_AUTOINDENT 1 100 112 #define VI_SHOWMATCH 2 … … 107 119 #define err_method (vi_setops & VI_ERR_METHOD) 108 120 109 110 static smallint editing; // >0 while we are editing a file111 // [code audit says "can be 0 or 1 only"]112 static smallint cmd_mode; // 0=command 1=insert 2=replace113 static smallint file_modified; // buffer contents changed114 static smallint last_file_modified = -1;115 static int fn_start; // index of first cmd line file name116 static int save_argc; // how many file names on cmd line117 static int cmdcnt; // repetition count118 static int rows, columns; // the terminal screen is this size119 static int crow, ccol, offset; // cursor is on Crow x Ccol with Horz Ofset120 static char *status_buffer; // mesages to the user121 #define STATUS_BUFFER_LEN 200122 static int have_status_msg; // is default edit status needed?123 // [don't make smallint!]124 static int last_status_cksum; // hash of current status line125 static char *current_filename; // current file name126 //static char *text, *end; // pointers to the user data in memory127 static char *screen; // pointer to the virtual screen buffer128 static int screensize; // and its size129 static char *screenbegin; // index into text[], of top line on the screen130 //static char *dot; // where all the action takes place131 static int tabstop;132 static char erase_char; // the users erase character133 static char last_input_char; // last char read from user134 static char last_forward_char; // last char searched for with 'f'135 136 121 #if ENABLE_FEATURE_VI_READONLY 137 //static smallint vi_readonly, readonly; 138 static smallint readonly_mode = 0; 122 smallint readonly_mode; 139 123 #define SET_READONLY_FILE(flags) ((flags) |= 0x01) 140 124 #define SET_READONLY_MODE(flags) ((flags) |= 0x02) 141 125 #define UNSET_READONLY_FILE(flags) ((flags) &= 0xfe) 142 126 #else 143 #define readonly_mode 0 144 #define SET_READONLY_FILE(flags) 145 #define SET_READONLY_MODE(flags) 146 #define UNSET_READONLY_FILE(flags) 147 #endif 127 #define SET_READONLY_FILE(flags) ((void)0) 128 #define SET_READONLY_MODE(flags) ((void)0) 129 #define UNSET_READONLY_FILE(flags) ((void)0) 130 #endif 131 132 smallint editing; // >0 while we are editing a file 133 // [code audit says "can be 0, 1 or 2 only"] 134 smallint cmd_mode; // 0=command 1=insert 2=replace 135 int file_modified; // buffer contents changed (counter, not flag!) 136 int last_file_modified; // = -1; 137 int fn_start; // index of first cmd line file name 138 int save_argc; // how many file names on cmd line 139 int cmdcnt; // repetition count 140 unsigned rows, columns; // the terminal screen is this size 141 #if ENABLE_FEATURE_VI_ASK_TERMINAL 142 int get_rowcol_error; 143 #endif 144 int crow, ccol; // cursor is on Crow x Ccol 145 int offset; // chars scrolled off the screen to the left 146 int have_status_msg; // is default edit status needed? 147 // [don't make smallint!] 148 int last_status_cksum; // hash of current status line 149 char *current_filename; 150 char *screenbegin; // index into text[], of top line on the screen 151 char *screen; // pointer to the virtual screen buffer 152 int screensize; // and its size 153 int tabstop; 154 int last_forward_char; // last char searched for with 'f' (int because of Unicode) 155 char erase_char; // the users erase character 156 char last_input_char; // last char read from user 148 157 149 158 #if ENABLE_FEATURE_VI_DOT_CMD 150 static smallint adding2q;// are we currently adding user input to q151 static char *last_modifying_cmd; // last modifying cmd for "." 152 static char *ioq, *ioq_start;// pointer to string for get_one_char to "read"159 smallint adding2q; // are we currently adding user input to q 160 int lmc_len; // length of last_modifying_cmd 161 char *ioq, *ioq_start; // pointer to string for get_one_char to "read" 153 162 #endif 154 163 #if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR 155 static int last_row;// where the cursor was last moved to164 int last_row; // where the cursor was last moved to 156 165 #endif 157 166 #if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME 158 static int my_pid; 159 #endif 160 #if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK 161 static char *modifying_cmds; // cmds that modify text[] 167 int my_pid; 162 168 #endif 163 169 #if ENABLE_FEATURE_VI_SEARCH 164 static char *last_search_pattern; // last pattern from a '/' or '?' search 165 #endif 166 167 /* Moving biggest data to malloced space... */ 168 struct globals { 169 /* many references - keep near the top of globals */ 170 char *text, *end; // pointers to the user data in memory 171 int text_size; // size of the allocated buffer 172 char *dot; // where all the action takes place 170 char *last_search_pattern; // last pattern from a '/' or '?' search 171 #endif 172 173 /* former statics */ 173 174 #if ENABLE_FEATURE_VI_YANKMARK 175 char *edit_file__cur_line; 176 #endif 177 int refresh__old_offset; 178 int format_edit_status__tot; 179 180 /* a few references only */ 181 #if ENABLE_FEATURE_VI_YANKMARK 182 int YDreg, Ureg; // default delete register and orig line for "U" 174 183 char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27 175 int YDreg, Ureg; // default delete register and orig line for "U"176 184 char *mark[28]; // user marks points somewhere in text[]- a-z and previous context '' 177 185 char *context_start, *context_end; 178 186 #endif 179 /* a few references only */180 187 #if ENABLE_FEATURE_VI_USE_SIGNALS 181 jmp_buf restart;// catch_sig()182 #endif 183 struct termios term_orig, term_vi; 188 sigjmp_buf restart; // catch_sig() 189 #endif 190 struct termios term_orig, term_vi; // remember what the cooked mode was 184 191 #if ENABLE_FEATURE_VI_COLON 185 192 char *initial_cmds[3]; // currently 2 entries, NULL terminated 186 193 #endif 194 // Should be just enough to hold a key sequence, 195 // but CRASHME mode uses it as generated command buffer too 196 #if ENABLE_FEATURE_VI_CRASHME 197 char readbuffer[128]; 198 #else 199 char readbuffer[KEYCODE_BUFFER_SIZE]; 200 #endif 201 #define STATUS_BUFFER_LEN 200 202 char status_buffer[STATUS_BUFFER_LEN]; // messages to the user 203 #if ENABLE_FEATURE_VI_DOT_CMD 204 char last_modifying_cmd[MAX_INPUT_LEN]; // last modifying cmd for "." 205 #endif 206 char get_input_line__buf[MAX_INPUT_LEN]; /* former static */ 207 208 char scr_out_buf[MAX_SCR_COLS + MAX_TABSTOP * 2]; 187 209 }; 188 210 #define G (*ptr_to_globals) … … 192 214 #define dot (G.dot ) 193 215 #define reg (G.reg ) 216 217 #define vi_setops (G.vi_setops ) 218 #define editing (G.editing ) 219 #define cmd_mode (G.cmd_mode ) 220 #define file_modified (G.file_modified ) 221 #define last_file_modified (G.last_file_modified ) 222 #define fn_start (G.fn_start ) 223 #define save_argc (G.save_argc ) 224 #define cmdcnt (G.cmdcnt ) 225 #define rows (G.rows ) 226 #define columns (G.columns ) 227 #define crow (G.crow ) 228 #define ccol (G.ccol ) 229 #define offset (G.offset ) 230 #define status_buffer (G.status_buffer ) 231 #define have_status_msg (G.have_status_msg ) 232 #define last_status_cksum (G.last_status_cksum ) 233 #define current_filename (G.current_filename ) 234 #define screen (G.screen ) 235 #define screensize (G.screensize ) 236 #define screenbegin (G.screenbegin ) 237 #define tabstop (G.tabstop ) 238 #define last_forward_char (G.last_forward_char ) 239 #define erase_char (G.erase_char ) 240 #define last_input_char (G.last_input_char ) 241 #if ENABLE_FEATURE_VI_READONLY 242 #define readonly_mode (G.readonly_mode ) 243 #else 244 #define readonly_mode 0 245 #endif 246 #define adding2q (G.adding2q ) 247 #define lmc_len (G.lmc_len ) 248 #define ioq (G.ioq ) 249 #define ioq_start (G.ioq_start ) 250 #define last_row (G.last_row ) 251 #define my_pid (G.my_pid ) 252 #define last_search_pattern (G.last_search_pattern) 253 254 #define edit_file__cur_line (G.edit_file__cur_line) 255 #define refresh__old_offset (G.refresh__old_offset) 256 #define format_edit_status__tot (G.format_edit_status__tot) 257 194 258 #define YDreg (G.YDreg ) 195 259 #define Ureg (G.Ureg ) … … 201 265 #define term_vi (G.term_vi ) 202 266 #define initial_cmds (G.initial_cmds ) 267 #define readbuffer (G.readbuffer ) 268 #define scr_out_buf (G.scr_out_buf ) 269 #define last_modifying_cmd (G.last_modifying_cmd ) 270 #define get_input_line__buf (G.get_input_line__buf) 271 272 #define INIT_G() do { \ 273 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 274 last_file_modified = -1; \ 275 /* "" but has space for 2 chars: */ \ 276 IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \ 277 } while (0) 278 203 279 204 280 static int init_text_buffer(char *); // init from file or create new 205 281 static void edit_file(char *); // edit one file 206 static void do_cmd( char); // execute a command282 static void do_cmd(int); // execute a command 207 283 static int next_tabstop(int); 208 284 static void sync_cursor(char *, int *, int *); // synchronize the screen cursor to dot … … 227 303 static char *new_screen(int, int); // malloc virtual screen memory 228 304 static char *char_insert(char *, char); // insert the char c at 'p' 229 static char *stupid_insert(char *, char); // stupidly insert the char c at 'p' 230 static char find_range(char **, char **, char); // return pointers for an object 305 // might reallocate text[]! use p += stupid_insert(p, ...), 306 // and be careful to not use pointers into potentially freed text[]! 307 static uintptr_t stupid_insert(char *, char); // stupidly insert the char c at 'p' 308 static int find_range(char **, char **, char); // return pointers for an object 231 309 static int st_test(char *, int, int, char *); // helper for skip_thing() 232 310 static char *skip_thing(char *, int, int, int); // skip some object 233 311 static char *find_pair(char *, char); // find matching pair () [] {} 234 312 static char *text_hole_delete(char *, char *); // at "p", delete a 'size' byte hole 235 static char *text_hole_make(char *, int); // at "p", make a 'size' byte hole 313 // might reallocate text[]! use p += text_hole_make(p, ...), 314 // and be careful to not use pointers into potentially freed text[]! 315 static uintptr_t text_hole_make(char *, int); // at "p", make a 'size' byte hole 236 316 static char *yank_delete(char *, char *, int, int); // yank text[] into register then delete 237 317 static void show_help(void); // display some help info 238 318 static void rawmode(void); // set "raw" mode on tty 239 319 static void cookmode(void); // return to "cooked" mode on tty 240 static int mysleep(int); // sleep for 'h' 1/100 seconds 241 static char readit(void); // read (maybe cursor) key from stdin 242 static char get_one_char(void); // read 1 char from stdin 320 // sleep for 'h' 1/100 seconds, return 1/0 if stdin is (ready for read)/(not ready) 321 static int mysleep(int); 322 static int readit(void); // read (maybe cursor) key from stdin 323 static int get_one_char(void); // read 1 char from stdin 243 324 static int file_size(const char *); // what is the byte size of "fn" 244 #if ENABLE_FEATURE_VI_READONLY 325 #if !ENABLE_FEATURE_VI_READONLY 326 #define file_insert(fn, p, update_ro_status) file_insert(fn, p) 327 #endif 328 // file_insert might reallocate text[]! 245 329 static int file_insert(const char *, char *, int); 246 #else247 static int file_insert(const char *, char *);248 #endif249 330 static int file_write(char *, char *, char *); 331 #if !ENABLE_FEATURE_VI_OPTIMIZE_CURSOR 332 #define place_cursor(a, b, optimize) place_cursor(a, b) 333 #endif 250 334 static void place_cursor(int, int, int); 251 335 static void screen_erase(void); 252 336 static void clear_to_eol(void); 253 337 static void clear_to_eos(void); 338 static void go_bottom_and_clear_to_eol(void); 254 339 static void standout_start(void); // send "start reverse video" sequence 255 340 static void standout_end(void); // send "end reverse video" sequence 256 341 static void flash(int); // flash the terminal screen 257 342 static void show_status_line(void); // put a message on the bottom line 258 static void psb(const char *, ...); // Print Status Buf259 static void psbs(const char *, ...); // Print Status Buf in standout mode260 static void n i(const char *); // display messages343 static void status_line(const char *, ...); // print to status buf 344 static void status_line_bold(const char *, ...); 345 static void not_implemented(const char *); // display "Not implemented" message 261 346 static int format_edit_status(void); // format file status on status line 262 347 static void redraw(int); // force a full screen refresh 263 static void format_line(char*, char*, int);348 static char* format_line(char* /*, int*/); 264 349 static void refresh(int); // update the terminal from screen[] 265 350 … … 292 377 #endif 293 378 #if ENABLE_FEATURE_VI_YANKMARK || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) || ENABLE_FEATURE_VI_CRASHME 294 static char *string_insert(char *, char *); // insert the string at 'p' 379 // might reallocate text[]! use p += string_insert(p, ...), 380 // and be careful to not use pointers into potentially freed text[]! 381 static uintptr_t string_insert(char *, const char *); // insert the string at 'p' 295 382 #endif 296 383 #if ENABLE_FEATURE_VI_YANKMARK … … 311 398 } 312 399 313 int vi_main(int argc, char **argv) ;400 int vi_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 314 401 int vi_main(int argc, char **argv) 315 402 { 316 403 int c; 317 RESERVE_CONFIG_BUFFER(STATUS_BUFFER, STATUS_BUFFER_LEN); 404 405 INIT_G(); 318 406 319 407 #if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME 320 408 my_pid = getpid(); 321 409 #endif 322 323 PTR_TO_GLOBALS = xzalloc(sizeof(G));324 325 410 #if ENABLE_FEATURE_VI_CRASHME 326 411 srand((long) my_pid); 327 412 #endif 328 329 status_buffer = STATUS_BUFFER;330 last_status_cksum = 0;331 text = NULL;332 333 413 #ifdef NO_SUCH_APPLET_YET 334 414 /* If we aren't "vi", we are "view" */ … … 338 418 #endif 339 419 340 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD; 341 #if ENABLE_FEATURE_VI_YANKMARK 342 memset(reg, 0, sizeof(reg)); // init the yank regs 343 #endif 344 #if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK 345 modifying_cmds = (char *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[] 346 #endif 347 420 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE; 348 421 // 1- process $HOME/.exrc file (not inplemented yet) 349 422 // 2- process EXINIT variable from environment … … 353 426 char *p = getenv("EXINIT"); 354 427 if (p && *p) 355 initial_cmds[0] = xstr dup(p);356 } 357 #endif 358 while ((c = getopt(argc, argv, "hCR " USE_FEATURE_VI_COLON("c:"))) != -1) {428 initial_cmds[0] = xstrndup(p, MAX_INPUT_LEN); 429 } 430 #endif 431 while ((c = getopt(argc, argv, "hCRH" IF_FEATURE_VI_COLON("c:"))) != -1) { 359 432 switch (c) { 360 433 #if ENABLE_FEATURE_VI_CRASHME … … 368 441 break; 369 442 #endif 370 //case 'r': // recover flag- ignore- we don't use tmp file371 //case 'x': // encryption flag- ignore372 //case 'c': // execute command first373 443 #if ENABLE_FEATURE_VI_COLON 374 444 case 'c': // cmd line vi command 375 445 if (*optarg) 376 initial_cmds[initial_cmds[0] != 0] = xstr dup(optarg);446 initial_cmds[initial_cmds[0] != 0] = xstrndup(optarg, MAX_INPUT_LEN); 377 447 break; 378 //case 'h': // help -- just use default 379 #endif 448 #endif 449 case 'H': 450 show_help(); 451 /* fall through */ 380 452 default: 381 show_help();453 bb_show_usage(); 382 454 return 1; 383 455 } … … 390 462 391 463 //----- This is the main file handling loop -------------- 392 if (optind >= argc) { 393 edit_file(0); 394 } else { 395 for (; optind < argc; optind++) { 396 edit_file(argv[optind]); 397 } 464 while (1) { 465 edit_file(argv[optind]); /* param might be NULL */ 466 if (++optind >= argc) 467 break; 398 468 } 399 469 //----------------------------------------------------------- … … 411 481 /* allocate/reallocate text buffer */ 412 482 free(text); 413 text_size = size * 2; 414 if (text_size < 10240) 415 text_size = 10240; // have a minimum size for new files 483 text_size = size + 10240; 416 484 screenbegin = dot = end = text = xzalloc(text_size); 417 485 … … 425 493 rc = 0; 426 494 } else { 427 rc = file_insert(fn, text 428 USE_FEATURE_VI_READONLY(, 1)); 495 rc = file_insert(fn, text, 1); 429 496 } 430 497 file_modified = 0; … … 437 504 } 438 505 439 static void edit_file(char * fn) 440 { 441 char c; 506 #if ENABLE_FEATURE_VI_WIN_RESIZE 507 static int query_screen_dimensions(void) 508 { 509 int err = get_terminal_width_height(STDIN_FILENO, &columns, &rows); 510 if (rows > MAX_SCR_ROWS) 511 rows = MAX_SCR_ROWS; 512 if (columns > MAX_SCR_COLS) 513 columns = MAX_SCR_COLS; 514 return err; 515 } 516 #else 517 # define query_screen_dimensions() (0) 518 #endif 519 520 static void edit_file(char *fn) 521 { 522 #if ENABLE_FEATURE_VI_YANKMARK 523 #define cur_line edit_file__cur_line 524 #endif 525 int c; 442 526 int size; 443 444 527 #if ENABLE_FEATURE_VI_USE_SIGNALS 445 528 int sig; 446 529 #endif 447 #if ENABLE_FEATURE_VI_YANKMARK 448 static char *cur_line; 449 #endif 450 451 editing = 1; // 0= exit, 1= one file, 2= multiple files 530 531 editing = 1; // 0 = exit, 1 = one file, 2 = multiple files 452 532 rawmode(); 453 533 rows = 24; 454 534 columns = 80; 455 535 size = 0; 456 if (ENABLE_FEATURE_VI_WIN_RESIZE) 457 get_terminal_width_height(0, &columns, &rows); 536 IF_FEATURE_VI_ASK_TERMINAL(G.get_rowcol_error =) query_screen_dimensions(); 537 #if ENABLE_FEATURE_VI_ASK_TERMINAL 538 if (G.get_rowcol_error /* TODO? && no input on stdin */) { 539 uint64_t k; 540 write1("\033[999;999H" "\033[6n"); 541 fflush_all(); 542 k = read_key(STDIN_FILENO, readbuffer, /*timeout_ms:*/ 100); 543 if ((int32_t)k == KEYCODE_CURSOR_POS) { 544 uint32_t rc = (k >> 32); 545 columns = (rc & 0x7fff); 546 if (columns > MAX_SCR_COLS) 547 columns = MAX_SCR_COLS; 548 rows = ((rc >> 16) & 0x7fff); 549 if (rows > MAX_SCR_ROWS) 550 rows = MAX_SCR_ROWS; 551 } 552 } 553 #endif 458 554 new_screen(rows, columns); // get memory for virtual screen 459 555 init_text_buffer(fn); … … 470 566 471 567 #if ENABLE_FEATURE_VI_USE_SIGNALS 472 catch_sig(0);568 signal(SIGINT, catch_sig); 473 569 signal(SIGWINCH, winch_sig); 474 570 signal(SIGTSTP, suspend_sig); 475 sig = s etjmp(restart);571 sig = sigsetjmp(restart, 1); 476 572 if (sig != 0) { 477 573 screenbegin = dot = text; … … 485 581 c = '\0'; 486 582 #if ENABLE_FEATURE_VI_DOT_CMD 487 free(last_modifying_cmd);488 583 free(ioq_start); 489 ioq = ioq_start = last_modifying_cmd = NULL; 584 ioq = ioq_start = NULL; 585 lmc_len = 0; 490 586 adding2q = 0; 491 587 #endif 492 redraw(FALSE); // dont force every col re-draw493 588 494 589 #if ENABLE_FEATURE_VI_COLON … … 497 592 int n = 0; 498 593 499 while ((p = initial_cmds[n]) ) {594 while ((p = initial_cmds[n]) != NULL) { 500 595 do { 501 596 q = p; 502 p = strchr(q, '\n');597 p = strchr(q, '\n'); 503 598 if (p) 504 599 while (*p == '\n') … … 513 608 } 514 609 #endif 610 redraw(FALSE); // dont force every col re-draw 515 611 //------This is the main Vi cmd handling loop ----------------------- 516 612 while (editing > 0) { … … 521 617 } else { 522 618 crashme = 0; 523 dot = string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string 619 string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string 620 dot = text; 524 621 refresh(FALSE); 525 622 } … … 537 634 // These are commands that change text[]. 538 635 // Remember the input for the "." command 539 if (!adding2q && ioq_start == 0 636 if (!adding2q && ioq_start == NULL 637 && cmd_mode == 0 // command mode 638 && c > '\0' // exclude NUL and non-ASCII chars 639 && c < 0x7f // (Unicode and such) 540 640 && strchr(modifying_cmds, c) 541 641 ) { … … 544 644 #endif 545 645 do_cmd(c); // execute the user command 546 // 646 547 647 // poll to see if there is input already waiting. if we are 548 648 // not able to display output fast enough to keep up, skip 549 649 // the display update until we catch up with input. 550 if ( mysleep(0) == 0) {551 // no input pending - so update output650 if (!readbuffer[0] && mysleep(0) == 0) { 651 // no input pending - so update output 552 652 refresh(FALSE); 553 653 show_status_line(); … … 560 660 //------------------------------------------------------------------- 561 661 562 place_cursor(rows, 0, FALSE); // go to bottom of screen 563 clear_to_eol(); // Erase to end of line 662 go_bottom_and_clear_to_eol(); 564 663 cookmode(); 664 #undef cur_line 565 665 } 566 666 567 667 //----- The Colon commands ------------------------------------- 568 668 #if ENABLE_FEATURE_VI_COLON 569 static char *get_one_address(char * 669 static char *get_one_address(char *p, int *addr) // get colon addr, if present 570 670 { 571 671 int st; 572 672 char *q; 573 574 #if ENABLE_FEATURE_VI_YANKMARK 575 char c; 576 #endif 577 #if ENABLE_FEATURE_VI_SEARCH 578 char *pat, buf[MAX_LINELEN]; 579 #endif 673 IF_FEATURE_VI_YANKMARK(char c;) 674 IF_FEATURE_VI_SEARCH(char *pat;) 580 675 581 676 *addr = -1; // assume no addr … … 584 679 q = begin_line(dot); 585 680 *addr = count_lines(text, q); 681 } 586 682 #if ENABLE_FEATURE_VI_YANKMARK 587 }else if (*p == '\'') { // is this a mark addr683 else if (*p == '\'') { // is this a mark addr 588 684 p++; 589 685 c = tolower(*p); … … 594 690 q = mark[(unsigned char) c]; 595 691 if (q != NULL) { // is mark valid 596 *addr = count_lines(text, q); // count lines692 *addr = count_lines(text, q); 597 693 } 598 694 } 695 } 599 696 #endif 600 697 #if ENABLE_FEATURE_VI_SEARCH 601 } else if (*p == '/') { // a search pattern 602 q = buf; 603 for (p++; *p; p++) { 604 if (*p == '/') 605 break; 606 *q++ = *p; 607 *q = '\0'; 608 } 609 pat = xstrdup(buf); // save copy of pattern 698 else if (*p == '/') { // a search pattern 699 q = strchrnul(++p, '/'); 700 pat = xstrndup(p, q - p); // save copy of pattern 701 p = q; 610 702 if (*p == '/') 611 703 p++; … … 615 707 } 616 708 free(pat); 617 #endif 618 } else if (*p == '$') { // the last line in file 709 } 710 #endif 711 else if (*p == '$') { // the last line in file 619 712 p++; 620 713 q = begin_line(end - 1); … … 623 716 sscanf(p, "%d%n", addr, &st); 624 717 p += st; 625 } else { // I don't reconise this626 // unrecogni sed address- assume -1718 } else { 719 // unrecognized address - assume -1 627 720 *addr = -1; 628 721 } … … 665 758 int l = strlen(opname) - 1; /* opname have + ' ' */ 666 759 760 // maybe strncmp? we had tons of erroneous strncasecmp's... 667 761 if (strncasecmp(a, opname, l) == 0 668 762 || strncasecmp(a, short_opname, 2) == 0 … … 676 770 #endif 677 771 678 static void colon(char * buf) 772 // buf must be no longer than MAX_INPUT_LEN! 773 static void colon(char *buf) 679 774 { 680 775 char c, *orig_buf, *buf1, *q, *r; 681 char *fn, cmd[MAX_ LINELEN], args[MAX_LINELEN];776 char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN]; 682 777 int i, l, li, ch, b, e; 683 int useforce = FALSE, forced = FALSE;778 int useforce, forced = FALSE; 684 779 685 780 // :3154 // if (-e line 3154) goto it else stay put … … 699 794 700 795 if (!buf[0]) 701 goto vc1;796 goto ret; 702 797 if (*buf == ':') 703 798 buf++; // move past the ':' … … 708 803 r = end - 1; 709 804 li = count_lines(text, end - 1); 710 fn = current_filename; // default to current file 711 memset(cmd, '\0', MAX_LINELEN); // clear cmd[] 712 memset(args, '\0', MAX_LINELEN); // clear args[] 805 fn = current_filename; 713 806 714 807 // look for optional address(es) :. :1 :1,9 :'q,'a :% … … 725 818 *buf1++ = *buf++; 726 819 } 820 *buf1 = '\0'; 727 821 // get any ARGuments 728 822 while (isblank(*buf)) 729 823 buf++; 730 824 strcpy(args, buf); 825 useforce = FALSE; 731 826 buf1 = last_char_is(cmd, '!'); 732 827 if (buf1) { … … 759 854 } 760 855 #if ENABLE_FEATURE_ALLOW_EXEC 761 else if ( strncmp(cmd, "!", 1) == 0) { // run a cmd856 else if (cmd[0] == '!') { // run a cmd 762 857 int retcode; 763 858 // :!ls run the <cmd> 764 alarm(0); // wait for input- no alarms 765 place_cursor(rows - 1, 0, FALSE); // go to Status line 766 clear_to_eol(); // clear the line 859 go_bottom_and_clear_to_eol(); 767 860 cookmode(); 768 861 retcode = system(orig_buf + 1); // run the cmd … … 771 864 rawmode(); 772 865 Hit_Return(); // let user see results 773 alarm(3); // done waiting for input 774 } 775 #endif 776 else if (strncmp(cmd, "=", i) == 0) { // where is the address 866 } 867 #endif 868 else if (cmd[0] == '=' && !cmd[1]) { // where is the address 777 869 if (b < 0) { // no addr given- use defaults 778 870 b = e = count_lines(text, dot); 779 871 } 780 psb("%d", b);781 } else if (strnc asecmp(cmd, "delete", i) == 0) { // delete lines872 status_line("%d", b); 873 } else if (strncmp(cmd, "delete", i) == 0) { // delete lines 782 874 if (b < 0) { // no addr given- use defaults 783 875 q = begin_line(dot); // assume .,. for the range … … 786 878 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines 787 879 dot_skip_over_ws(); 788 } else if (strnc asecmp(cmd, "edit", i) == 0) { // Edit a file880 } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file 789 881 // don't edit, if the current file has been modified 790 if (file_modified && ! 791 psbs("No write since last change (:edit! overrides)");792 goto vc1;882 if (file_modified && !useforce) { 883 status_line_bold("No write since last change (:edit! overrides)"); 884 goto ret; 793 885 } 794 886 if (args[0]) { … … 800 892 } else { 801 893 // no user file name, no current name- punt 802 psbs("No current filename");803 goto vc1;894 status_line_bold("No current filename"); 895 goto ret; 804 896 } 805 897 806 898 if (init_text_buffer(fn) < 0) 807 goto vc1;899 goto ret; 808 900 809 901 #if ENABLE_FEATURE_VI_YANKMARK … … 819 911 // how many lines in text[]? 820 912 li = count_lines(text, end - 1); 821 psb("\"%s\"%s"822 USE_FEATURE_VI_READONLY("%s")913 status_line("\"%s\"%s" 914 IF_FEATURE_VI_READONLY("%s") 823 915 " %dL, %dC", current_filename, 824 916 (file_size(fn) < 0 ? " [New file]" : ""), 825 USE_FEATURE_VI_READONLY(917 IF_FEATURE_VI_READONLY( 826 918 ((readonly_mode) ? " [Readonly]" : ""), 827 919 ) 828 920 li, ch); 829 } else if (strnc asecmp(cmd, "file", i) == 0) { // what File is this921 } else if (strncmp(cmd, "file", i) == 0) { // what File is this 830 922 if (b != -1 || e != -1) { 831 ni("No address allowed on this command");832 goto vc1;923 status_line_bold("No address allowed on this command"); 924 goto ret; 833 925 } 834 926 if (args[0]) { … … 840 932 last_status_cksum = 0; // force status update 841 933 } 842 } else if (strnc asecmp(cmd, "features", i) == 0) { // what features are available934 } else if (strncmp(cmd, "features", i) == 0) { // what features are available 843 935 // print out values of all features 844 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen 845 clear_to_eol(); // clear the line 936 go_bottom_and_clear_to_eol(); 846 937 cookmode(); 847 938 show_help(); 848 939 rawmode(); 849 940 Hit_Return(); 850 } else if (strnc asecmp(cmd, "list", i) == 0) { // literal print line941 } else if (strncmp(cmd, "list", i) == 0) { // literal print line 851 942 if (b < 0) { // no addr given- use defaults 852 943 q = begin_line(dot); // assume .,. for the range 853 944 r = end_line(dot); 854 945 } 855 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen 856 clear_to_eol(); // clear the line 946 go_bottom_and_clear_to_eol(); 857 947 puts("\r"); 858 948 for (; q <= r; q++) { … … 864 954 c = '.'; 865 955 standout_start(); 866 956 } 867 957 if (c == '\n') { 868 958 write1("$\r"); 869 959 } else if (c < ' ' || c == 127) { 870 putchar('^');960 bb_putchar('^'); 871 961 if (c == 127) 872 962 c = '?'; … … 874 964 c += '@'; 875 965 } 876 putchar(c);966 bb_putchar(c); 877 967 if (c_is_no_print) 878 968 standout_end(); 879 969 } 880 #if ENABLE_FEATURE_VI_SET881 vc2:882 #endif883 970 Hit_Return(); 884 } else if (strnc asecmp(cmd, "quit", i) == 0 // Quit885 || strnc asecmp(cmd, "next", i) == 0 // edit next file971 } else if (strncmp(cmd, "quit", i) == 0 // quit 972 || strncmp(cmd, "next", i) == 0 // edit next file 886 973 ) { 974 int n; 887 975 if (useforce) { 888 976 // force end of argv list … … 891 979 } 892 980 editing = 0; 893 goto vc1;981 goto ret; 894 982 } 895 983 // don't exit if the file been modified 896 984 if (file_modified) { 897 psbs("No write since last change (:%s! overrides)",985 status_line_bold("No write since last change (:%s! overrides)", 898 986 (*cmd == 'q' ? "quit" : "next")); 899 goto vc1;987 goto ret; 900 988 } 901 989 // are there other file to edit 902 if (*cmd == 'q' && optind < save_argc - 1) { 903 psbs("%d more file to edit", (save_argc - optind - 1)); 904 goto vc1; 905 } 906 if (*cmd == 'n' && optind >= save_argc - 1) { 907 psbs("No more files to edit"); 908 goto vc1; 990 n = save_argc - optind - 1; 991 if (*cmd == 'q' && n > 0) { 992 status_line_bold("%d more file(s) to edit", n); 993 goto ret; 994 } 995 if (*cmd == 'n' && n <= 0) { 996 status_line_bold("No more files to edit"); 997 goto ret; 909 998 } 910 999 editing = 0; 911 } else if (strnc asecmp(cmd, "read", i) == 0) { // read file into text[]1000 } else if (strncmp(cmd, "read", i) == 0) { // read file into text[] 912 1001 fn = args; 913 1002 if (!fn[0]) { 914 psbs("No filename given");915 goto vc1;1003 status_line_bold("No filename given"); 1004 goto ret; 916 1005 } 917 1006 if (b < 0) { // no addr given- use defaults … … 921 1010 if (b != 0) 922 1011 q = next_line(q); 923 ch = file_insert(fn, q USE_FEATURE_VI_READONLY(, 0)); 1012 { // dance around potentially-reallocated text[] 1013 uintptr_t ofs = q - text; 1014 ch = file_insert(fn, q, 0); 1015 q = text + ofs; 1016 } 924 1017 if (ch < 0) 925 goto vc1; // nothing was inserted1018 goto ret; // nothing was inserted 926 1019 // how many lines in text[]? 927 1020 li = count_lines(q, q + ch - 1); 928 psb("\"%s\""929 USE_FEATURE_VI_READONLY("%s")1021 status_line("\"%s\"" 1022 IF_FEATURE_VI_READONLY("%s") 930 1023 " %dL, %dC", fn, 931 USE_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)1024 IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),) 932 1025 li, ch); 933 1026 if (ch > 0) { … … 935 1028 if (q <= dot) 936 1029 dot += ch; 937 file_modified++;938 } 939 } else if (strnc asecmp(cmd, "rewind", i) == 0) { // rewind cmd line args940 if (file_modified && ! 941 psbs("No write since last change (:rewind! overrides)");1030 /*file_modified++; - done by file_insert */ 1031 } 1032 } else if (strncmp(cmd, "rewind", i) == 0) { // rewind cmd line args 1033 if (file_modified && !useforce) { 1034 status_line_bold("No write since last change (:rewind! overrides)"); 942 1035 } else { 943 1036 // reset the filenames to edit … … 946 1039 } 947 1040 #if ENABLE_FEATURE_VI_SET 948 } else if (strnc asecmp(cmd, "set", i) == 0) { // set or clear features1041 } else if (strncmp(cmd, "set", i) == 0) { // set or clear features 949 1042 #if ENABLE_FEATURE_VI_SETOPTS 950 1043 char *argp; … … 954 1047 if (!args[0] || strcasecmp(args, "all") == 0) { 955 1048 // print out values of all options 956 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen957 clear_to_eol(); // clear the line958 printf("----------------------------------------\r\n");959 1049 #if ENABLE_FEATURE_VI_SETOPTS 960 if (!autoindent) 961 printf("no"); 962 printf("autoindent "); 963 if (!err_method) 964 printf("no"); 965 printf("flash "); 966 if (!ignorecase) 967 printf("no"); 968 printf("ignorecase "); 969 if (!showmatch) 970 printf("no"); 971 printf("showmatch "); 972 printf("tabstop=%d ", tabstop); 973 #endif 974 printf("\r\n"); 975 goto vc2; 1050 status_line_bold( 1051 "%sautoindent " 1052 "%sflash " 1053 "%signorecase " 1054 "%sshowmatch " 1055 "tabstop=%u", 1056 autoindent ? "" : "no", 1057 err_method ? "" : "no", 1058 ignorecase ? "" : "no", 1059 showmatch ? "" : "no", 1060 tabstop 1061 ); 1062 #endif 1063 goto ret; 976 1064 } 977 1065 #if ENABLE_FEATURE_VI_SETOPTS 978 1066 argp = args; 979 1067 while (*argp) { 980 if (strnc asecmp(argp, "no", 2) == 0)1068 if (strncmp(argp, "no", 2) == 0) 981 1069 i = 2; // ":set noautoindent" 982 1070 setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT); 983 setops(argp, "flash " , i, "fl", VI_ERR_METHOD);1071 setops(argp, "flash " , i, "fl", VI_ERR_METHOD); 984 1072 setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE); 985 setops(argp, "showmatch " , i, "ic", VI_SHOWMATCH);986 /* tabstopXXXX */987 if (strncasecmp(argp + i, "tabstop=%d ", 7) == 0) {988 sscanf( strchr(argp + i, '='), "tabstop=%d" + 7, &ch);989 if ( ch > 0 && ch < columns - 1)990 tabstop = ch;1073 setops(argp, "showmatch " , i, "sm", VI_SHOWMATCH ); 1074 if (strncmp(argp + i, "tabstop=", 8) == 0) { 1075 int t = 0; 1076 sscanf(argp + i+8, "%u", &t); 1077 if (t > 0 && t <= MAX_TABSTOP) 1078 tabstop = t; 991 1079 } 992 while (*argp && *argp != ' ') 993 argp++; // skip to arg delimiter (i.e. blank) 994 while (*argp && *argp == ' ') 995 argp++; // skip all delimiting blanks 1080 argp = skip_non_whitespace(argp); 1081 argp = skip_whitespace(argp); 996 1082 } 997 1083 #endif /* FEATURE_VI_SETOPTS */ 998 1084 #endif /* FEATURE_VI_SET */ 999 1085 #if ENABLE_FEATURE_VI_SEARCH 1000 } else if ( strncasecmp(cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern1086 } else if (cmd[0] == 's') { // substitute a pattern with a replacement pattern 1001 1087 char *ls, *F, *R; 1002 1088 int gflag; … … 1009 1095 F = orig_buf + 2; // start of "find" 1010 1096 R = strchr(F, c); // middle delimiter 1011 if (!R) goto colon_s_fail; 1097 if (!R) 1098 goto colon_s_fail; 1012 1099 *R++ = '\0'; // terminate "find" 1013 1100 buf1 = strchr(R, c); 1014 if (!buf1) goto colon_s_fail; 1101 if (!buf1) 1102 goto colon_s_fail; 1015 1103 *buf1++ = '\0'; // terminate "replace" 1016 1104 if (*buf1 == 'g') { // :s/foo/bar/g … … 1030 1118 buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find" 1031 1119 if (buf1) { 1120 uintptr_t bias; 1032 1121 // we found the "find" pattern - delete it 1033 1122 text_hole_delete(buf1, buf1 + strlen(F) - 1); 1034 1123 // inset the "replace" patern 1035 string_insert(buf1, R); // insert the string 1124 bias = string_insert(buf1, R); // insert the string 1125 buf1 += bias; 1126 ls += bias; 1127 /*q += bias; - recalculated anyway */ 1036 1128 // check for "global" :s/foo/bar/g 1037 1129 if (gflag == 1) { … … 1045 1137 } 1046 1138 #endif /* FEATURE_VI_SEARCH */ 1047 } else if (strnc asecmp(cmd, "version", i) == 0) { // show software version1048 psb("%s",BB_VER " " BB_BT);1049 } else if (strnc asecmp(cmd, "write", i) == 0 // write text to file1050 || strnc asecmp(cmd, "wq", i) == 01051 || strnc asecmp(cmd, "wn", i) == 01052 || strncasecmp(cmd, "x", i) == 01139 } else if (strncmp(cmd, "version", i) == 0) { // show software version 1140 status_line(BB_VER " " BB_BT); 1141 } else if (strncmp(cmd, "write", i) == 0 // write text to file 1142 || strncmp(cmd, "wq", i) == 0 1143 || strncmp(cmd, "wn", i) == 0 1144 || (cmd[0] == 'x' && !cmd[1]) 1053 1145 ) { 1054 1146 // is there a file name to write to? … … 1058 1150 #if ENABLE_FEATURE_VI_READONLY 1059 1151 if (readonly_mode && !useforce) { 1060 psbs("\"%s\" File is read only", fn);1061 goto vc3;1152 status_line_bold("\"%s\" File is read only", fn); 1153 goto ret; 1062 1154 } 1063 1155 #endif … … 1081 1173 if (l < 0) { 1082 1174 if (l == -1) 1083 psbs("\"%s\" %s", fn, strerror(errno));1175 status_line_bold("\"%s\" %s", fn, strerror(errno)); 1084 1176 } else { 1085 psb("\"%s\" %dL, %dC", fn, li, l);1177 status_line("\"%s\" %dL, %dC", fn, li, l); 1086 1178 if (q == text && r == end - 1 && l == ch) { 1087 1179 file_modified = 0; 1088 1180 last_file_modified = -1; 1089 1181 } 1090 if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n' || 1091 cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N') 1092 && l == ch) { 1182 if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n' 1183 || cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N' 1184 ) 1185 && l == ch 1186 ) { 1093 1187 editing = 0; 1094 1188 } 1095 1189 } 1096 #if ENABLE_FEATURE_VI_READONLY1097 vc3:;1098 #endif1099 1190 #if ENABLE_FEATURE_VI_YANKMARK 1100 } else if (strnc asecmp(cmd, "yank", i) == 0) { // yank lines1191 } else if (strncmp(cmd, "yank", i) == 0) { // yank lines 1101 1192 if (b < 0) { // no addr given- use defaults 1102 1193 q = begin_line(dot); // assume .,. for the range … … 1105 1196 text_yank(q, r, YDreg); 1106 1197 li = count_lines(q, r); 1107 psb("Yank %d lines (%d chars) into [%c]",1198 status_line("Yank %d lines (%d chars) into [%c]", 1108 1199 li, strlen(reg[YDreg]), what_reg()); 1109 1200 #endif 1110 1201 } else { 1111 1202 // cmd unknown 1112 n i(cmd);1113 } 1114 vc1:1203 not_implemented(cmd); 1204 } 1205 ret: 1115 1206 dot = bound_dot(dot); // make sure "dot" is valid 1116 1207 return; 1117 1208 #if ENABLE_FEATURE_VI_SEARCH 1118 1209 colon_s_fail: 1119 psb(":s expression missing delimiters");1210 status_line(":s expression missing delimiters"); 1120 1211 #endif 1121 1212 } … … 1125 1216 static void Hit_Return(void) 1126 1217 { 1127 charc;1128 1129 standout_start(); // start reverse video1218 int c; 1219 1220 standout_start(); 1130 1221 write1("[Hit return to continue]"); 1131 standout_end(); // end reverse video1132 while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */1133 ;1222 standout_end(); 1223 while ((c = get_one_char()) != '\n' && c != '\r') 1224 continue; 1134 1225 redraw(TRUE); // force redraw all 1135 1226 } … … 1141 1232 1142 1233 //----- Synchronize the cursor to Dot -------------------------- 1143 static void sync_cursor(char *d, int *row, int *col)1234 static NOINLINE void sync_cursor(char *d, int *row, int *col) 1144 1235 { 1145 1236 char *beg_cur; // begin and end of "d" line 1146 char *end_scr; // begin and end of screen1147 1237 char *tp; 1148 1238 int cnt, ro, co; … … 1150 1240 beg_cur = begin_line(d); // first char of cur line 1151 1241 1152 end_scr = end_screen(); // last char of screen1153 1154 1242 if (beg_cur < screenbegin) { 1155 // "d" is before 1243 // "d" is before top line on screen 1156 1244 // how many lines do we have to move 1157 1245 cnt = count_lines(beg_cur, screenbegin); … … 1164 1252 } 1165 1253 } 1166 } else if (beg_cur > end_scr) { 1167 // "d" is after bottom line on screen 1168 // how many lines do we have to move 1169 cnt = count_lines(end_scr, beg_cur); 1170 if (cnt > (rows - 1) / 2) 1171 goto sc1; // too many lines 1172 for (ro = 0; ro < cnt - 1; ro++) { 1173 // move screen begin the same amount 1174 screenbegin = next_line(screenbegin); 1175 // now, move the end of screen 1176 end_scr = next_line(end_scr); 1177 end_scr = end_line(end_scr); 1254 } else { 1255 char *end_scr; // begin and end of screen 1256 end_scr = end_screen(); // last char of screen 1257 if (beg_cur > end_scr) { 1258 // "d" is after bottom line on screen 1259 // how many lines do we have to move 1260 cnt = count_lines(end_scr, beg_cur); 1261 if (cnt > (rows - 1) / 2) 1262 goto sc1; // too many lines 1263 for (ro = 0; ro < cnt - 1; ro++) { 1264 // move screen begin the same amount 1265 screenbegin = next_line(screenbegin); 1266 // now, move the end of screen 1267 end_scr = next_line(end_scr); 1268 end_scr = end_line(end_scr); 1269 } 1178 1270 } 1179 1271 } … … 1188 1280 // find out what col "d" is on 1189 1281 co = 0; 1190 do {// drive "co" to correct column1191 if (*tp == '\n' || *tp == '\0')1282 while (tp < d) { // drive "co" to correct column 1283 if (*tp == '\n') //vda || *tp == '\0') 1192 1284 break; 1193 1285 if (*tp == '\t') { 1194 if (d == tp && cmd_mode) { /* handle tabs like real vi */ 1286 // handle tabs like real vi 1287 if (d == tp && cmd_mode) { 1195 1288 break; 1196 } else {1197 co = next_tabstop(co);1198 1289 } 1199 } else if (*tp < ' ' || *tp == 127) { 1200 co++; // display as ^X, use 2 columns 1201 } 1202 } while (tp++ < d && ++co); 1290 co = next_tabstop(co); 1291 } else if ((unsigned char)*tp < ' ' || *tp == 0x7f) { 1292 co++; // display as ^X, use 2 columns 1293 } 1294 co++; 1295 tp++; 1296 } 1203 1297 1204 1298 // "co" is the column where "dot" is. … … 1233 1327 1234 1328 //----- Text Movement Routines --------------------------------- 1235 static char *begin_line(char * p) // return pointer to first char cur line 1236 { 1237 while (p > text && p[-1] != '\n') 1238 p--; // go to cur line B-o-l 1329 static char *begin_line(char *p) // return pointer to first char cur line 1330 { 1331 if (p > text) { 1332 p = memrchr(text, '\n', p - text); 1333 if (!p) 1334 return text; 1335 return p + 1; 1336 } 1239 1337 return p; 1240 1338 } 1241 1339 1242 static char *end_line(char * p) // return pointer to NL of cur line line 1243 { 1244 while (p < end - 1 && *p != '\n') 1245 p++; // go to cur line E-o-l 1340 static char *end_line(char *p) // return pointer to NL of cur line 1341 { 1342 if (p < end - 1) { 1343 p = memchr(p, '\n', end - p - 1); 1344 if (!p) 1345 return end - 1; 1346 } 1246 1347 return p; 1247 1348 } 1248 1349 1249 static inline char *dollar_line(char * p) // return pointer to just before NL line 1250 { 1251 while (p < end - 1 && *p != '\n') 1252 p++; // go to cur line E-o-l 1350 static char *dollar_line(char *p) // return pointer to just before NL line 1351 { 1352 p = end_line(p); 1253 1353 // Try to stay off of the Newline 1254 1354 if (*p == '\n' && (p - begin_line(p)) > 0) … … 1257 1357 } 1258 1358 1259 static char *prev_line(char * 1359 static char *prev_line(char *p) // return pointer first char prev line 1260 1360 { 1261 1361 p = begin_line(p); // goto begining of cur line 1262 if (p [-1] == '\n' && p > text)1362 if (p > text && p[-1] == '\n') 1263 1363 p--; // step to prev line 1264 1364 p = begin_line(p); // goto begining of prev line … … 1266 1366 } 1267 1367 1268 static char *next_line(char * 1368 static char *next_line(char *p) // return pointer first char next line 1269 1369 { 1270 1370 p = end_line(p); 1271 if ( *p == '\n' && p < end - 1)1371 if (p < end - 1 && *p == '\n') 1272 1372 p++; // step to next line 1273 1373 return p; … … 1288 1388 } 1289 1389 1290 static int count_lines(char * start, char * stop) // count line from start to stop 1390 // count line from start to stop 1391 static int count_lines(char *start, char *stop) 1291 1392 { 1292 1393 char *q; 1293 1394 int cnt; 1294 1395 1295 if (stop < start) { 1396 if (stop < start) { // start and stop are backwards- reverse them 1296 1397 q = start; 1297 1398 start = stop; … … 1299 1400 } 1300 1401 cnt = 0; 1301 stop = end_line(stop); // get to end of this line 1302 for (q = start; q <= stop && q <= end - 1; q++) { 1303 if (*q == '\n') 1402 stop = end_line(stop); 1403 while (start <= stop && start <= end - 1) { 1404 start = end_line(start); 1405 if (*start == '\n') 1304 1406 cnt++; 1407 start++; 1305 1408 } 1306 1409 return cnt; … … 1340 1443 } 1341 1444 1342 static char *move_to_col(char * 1445 static char *move_to_col(char *p, int l) 1343 1446 { 1344 1447 int co; … … 1346 1449 p = begin_line(p); 1347 1450 co = 0; 1348 do{1349 if (*p == '\n' || *p == '\0')1451 while (co < l && p < end) { 1452 if (*p == '\n') //vda || *p == '\0') 1350 1453 break; 1351 1454 if (*p == '\t') { 1352 1455 co = next_tabstop(co); 1353 1456 } else if (*p < ' ' || *p == 127) { 1354 co++; // display as ^X, use 2 columns 1355 } 1356 } while (++co <= l && p++ < end); 1457 co++; // display as ^X, use 2 columns 1458 } 1459 co++; 1460 p++; 1461 } 1357 1462 return p; 1358 1463 } … … 1375 1480 if (dir < 0) { 1376 1481 // scroll Backwards 1377 // ctrl-Y 1482 // ctrl-Y scroll up one line 1378 1483 screenbegin = prev_line(screenbegin); 1379 1484 } else { 1380 1485 // scroll Forwards 1381 // ctrl-E 1486 // ctrl-E scroll down one line 1382 1487 screenbegin = next_line(screenbegin); 1383 1488 } … … 1404 1509 } 1405 1510 1406 static char *bound_dot(char * 1511 static char *bound_dot(char *p) // make sure text[0] <= P < "end" 1407 1512 { 1408 1513 if (p >= end && end > text) { … … 1447 1552 1448 1553 #if ENABLE_FEATURE_VI_SEARCH 1449 static int mycmp(const char * s1, const char * s2, int len) 1450 { 1451 int i; 1452 1453 i = strncmp(s1, s2, len); 1554 static int mycmp(const char *s1, const char *s2, int len) 1555 { 1454 1556 if (ENABLE_FEATURE_VI_SETOPTS && ignorecase) { 1455 i =strncasecmp(s1, s2, len);1456 } 1457 return i;1557 return strncasecmp(s1, s2, len); 1558 } 1559 return strncmp(s1, s2, len); 1458 1560 } 1459 1561 1460 1562 // search for pattern starting at p 1461 static char *char_search(char * p, const char *pat, int dir, int range)1563 static char *char_search(char *p, const char *pat, int dir, int range) 1462 1564 { 1463 1565 #ifndef REGEX_SEARCH … … 1517 1619 if (q != 0) { 1518 1620 // The pattern was not compiled 1519 psbs("bad search pattern: \"%s\": %s", pat, q);1621 status_line_bold("bad search pattern: \"%s\": %s", pat, q); 1520 1622 i = 0; // return p if pattern not compiled 1521 1623 goto cs1; … … 1552 1654 #endif /* FEATURE_VI_SEARCH */ 1553 1655 1554 static char *char_insert(char * 1656 static char *char_insert(char *p, char c) // insert the char c at 'p' 1555 1657 { 1556 1658 if (c == 22) { // Is this an ctrl-V? 1557 p = stupid_insert(p, '^'); // use ^ to indicate literal next 1558 p--; // backup onto ^ 1659 p += stupid_insert(p, '^'); // use ^ to indicate literal next 1559 1660 refresh(FALSE); // show the ^ 1560 1661 c = get_one_char(); 1561 1662 *p = c; 1562 1663 p++; 1563 file_modified++; // has the file been modified1664 file_modified++; 1564 1665 } else if (c == 27) { // Is this an ESC? 1565 1666 cmd_mode = 0; … … 1583 1684 c = '\n'; // translate \r to \n 1584 1685 sp = p; // remember addr of insert 1585 p =stupid_insert(p, c); // insert the char1686 p += 1 + stupid_insert(p, c); // insert the char 1586 1687 #if ENABLE_FEATURE_VI_SETOPTS 1587 1688 if (showmatch && strchr(")]}", *sp) != NULL) { … … 1590 1691 if (autoindent && c == '\n') { // auto indent the new line 1591 1692 char *q; 1592 1593 q = prev_line(p); // use prev line as templet 1594 for (; isblank(*q); q++) { 1595 p = stupid_insert(p, *q); // insert the char 1693 size_t len; 1694 q = prev_line(p); // use prev line as template 1695 len = strspn(q, " \t"); // space or tab 1696 if (len) { 1697 uintptr_t bias; 1698 bias = text_hole_make(p, len); 1699 p += bias; 1700 q += bias; 1701 memcpy(p, q, len); 1702 p += len; 1596 1703 } 1597 1704 } … … 1601 1708 } 1602 1709 1603 static char *stupid_insert(char * p, char c) // stupidly insert the char c at 'p' 1604 { 1605 p = text_hole_make(p, 1); 1606 if (p != 0) { 1607 *p = c; 1608 file_modified++; // has the file been modified 1609 p++; 1610 } 1611 return p; 1612 } 1613 1614 static char find_range(char ** start, char ** stop, char c) 1615 { 1616 char *save_dot, *p, *q; 1617 int cnt; 1710 // might reallocate text[]! use p += stupid_insert(p, ...), 1711 // and be careful to not use pointers into potentially freed text[]! 1712 static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at 'p' 1713 { 1714 uintptr_t bias; 1715 bias = text_hole_make(p, 1); 1716 p += bias; 1717 *p = c; 1718 //file_modified++; - done by text_hole_make() 1719 return bias; 1720 } 1721 1722 static int find_range(char **start, char **stop, char c) 1723 { 1724 char *save_dot, *p, *q, *t; 1725 int cnt, multiline = 0; 1618 1726 1619 1727 save_dot = dot; … … 1627 1735 } 1628 1736 q = end_line(q); 1629 } else if (strchr("^%$0bBeEft ", c)) {1737 } else if (strchr("^%$0bBeEfth\b\177", c)) { 1630 1738 // These cmds operate on char positions 1631 1739 do_cmd(c); // execute movement cmd … … 1656 1764 q = dot; 1657 1765 } else { 1658 c = 27; // error- return an ESC char 1659 //break; 1660 } 1766 // nothing -- this causes any other values of c to 1767 // represent the one-character range under the 1768 // cursor. this is correct for ' ' and 'l', but 1769 // perhaps no others. 1770 // 1771 } 1772 if (q < p) { 1773 t = q; 1774 q = p; 1775 p = t; 1776 } 1777 1778 // backward char movements don't include start position 1779 if (q > p && strchr("^0bBh\b\177", c)) q--; 1780 1781 multiline = 0; 1782 for (t = p; t <= q; t++) { 1783 if (*t == '\n') { 1784 multiline = 1; 1785 break; 1786 } 1787 } 1788 1661 1789 *start = p; 1662 1790 *stop = q; 1663 if (q < p) {1664 *start = q;1665 *stop = p;1666 }1667 1791 dot = save_dot; 1668 return c;1669 } 1670 1671 static int st_test(char * p, int type, int dir, char *tested)1792 return multiline; 1793 } 1794 1795 static int st_test(char *p, int type, int dir, char *tested) 1672 1796 { 1673 1797 char c, c0, ci; … … 1681 1805 if (type == S_BEFORE_WS) { 1682 1806 c = ci; 1683 test = ( (!isspace(c)) || c == '\n');1807 test = (!isspace(c) || c == '\n'); 1684 1808 } 1685 1809 if (type == S_TO_WS) { 1686 1810 c = c0; 1687 test = ( (!isspace(c)) || c == '\n');1811 test = (!isspace(c) || c == '\n'); 1688 1812 } 1689 1813 if (type == S_OVER_WS) { 1690 1814 c = c0; 1691 test = ((isspace(c)));1815 test = isspace(c); 1692 1816 } 1693 1817 if (type == S_END_PUNCT) { 1694 1818 c = ci; 1695 test = ((ispunct(c)));1819 test = ispunct(c); 1696 1820 } 1697 1821 if (type == S_END_ALNUM) { 1698 1822 c = ci; 1699 test = ( (isalnum(c)) || c == '_');1823 test = (isalnum(c) || c == '_'); 1700 1824 } 1701 1825 *tested = c; … … 1703 1827 } 1704 1828 1705 static char *skip_thing(char * 1829 static char *skip_thing(char *p, int linecnt, int dir, int type) 1706 1830 { 1707 1831 char c; … … 1721 1845 1722 1846 // find matching char of pair () [] {} 1723 static char *find_pair(char * p,char c)1847 static char *find_pair(char *p, const char c) 1724 1848 { 1725 1849 char match, *q; … … 1730 1854 dir = 1; // assume forward 1731 1855 switch (c) { 1732 case '(': 1733 match = ')'; 1734 break; 1735 case '[': 1736 match = ']'; 1737 break; 1738 case '{': 1739 match = '}'; 1740 break; 1741 case ')': 1742 match = '('; 1743 dir = -1; 1744 break; 1745 case ']': 1746 match = '['; 1747 dir = -1; 1748 break; 1749 case '}': 1750 match = '{'; 1751 dir = -1; 1752 break; 1856 case '(': match = ')'; break; 1857 case '[': match = ']'; break; 1858 case '{': match = '}'; break; 1859 case ')': match = '('; dir = -1; break; 1860 case ']': match = '['; dir = -1; break; 1861 case '}': match = '{'; dir = -1; break; 1753 1862 } 1754 1863 for (q = p + dir; text <= q && q < end; q += dir) { … … 1768 1877 #if ENABLE_FEATURE_VI_SETOPTS 1769 1878 // show the matching char of a pair, () [] {} 1770 static void showmatching(char * 1879 static void showmatching(char *p) 1771 1880 { 1772 1881 char *q, *save_dot; … … 1788 1897 #endif /* FEATURE_VI_SETOPTS */ 1789 1898 1790 // open a hole in text[] 1791 static char *text_hole_make(char * p, int size) // at "p", make a 'size' byte hole 1792 { 1793 char *src, *dest; 1794 int cnt; 1899 // open a hole in text[] 1900 // might reallocate text[]! use p += text_hole_make(p, ...), 1901 // and be careful to not use pointers into potentially freed text[]! 1902 static uintptr_t text_hole_make(char *p, int size) // at "p", make a 'size' byte hole 1903 { 1904 uintptr_t bias = 0; 1795 1905 1796 1906 if (size <= 0) 1797 goto thm0; 1798 src = p; 1799 dest = p + size; 1800 cnt = end - src; // the rest of buffer 1801 if ( ((end + size) >= (text + text_size)) // TODO: realloc here 1802 || memmove(dest, src, cnt) != dest) { 1803 psbs("can't create room for new characters"); 1804 p = NULL; 1805 goto thm0; 1806 } 1907 return bias; 1908 end += size; // adjust the new END 1909 if (end >= (text + text_size)) { 1910 char *new_text; 1911 text_size += end - (text + text_size) + 10240; 1912 new_text = xrealloc(text, text_size); 1913 bias = (new_text - text); 1914 screenbegin += bias; 1915 dot += bias; 1916 end += bias; 1917 p += bias; 1918 text = new_text; 1919 } 1920 memmove(p + size, p, end - size - p); 1807 1921 memset(p, ' ', size); // clear new hole 1808 end += size; // adjust the new END 1809 file_modified++; // has the file been modified 1810 thm0: 1811 return p; 1922 file_modified++; 1923 return bias; 1812 1924 } 1813 1925 1814 1926 // close a hole in text[] 1815 static char *text_hole_delete(char * p, char * q) // delete "p" thru"q", inclusive1927 static char *text_hole_delete(char *p, char *q) // delete "p" through "q", inclusive 1816 1928 { 1817 1929 char *src, *dest; … … 1834 1946 if (src >= end) 1835 1947 goto thd_atend; // just delete the end of the buffer 1836 if (memmove(dest, src, cnt) != dest) { 1837 psbs("can't delete the character"); 1838 } 1948 memmove(dest, src, cnt); 1839 1949 thd_atend: 1840 1950 end = end - hole_size; // adjust the new END … … 1843 1953 if (end <= text) 1844 1954 dest = end = text; // keep pointers valid 1845 file_modified++; // has the file been modified1955 file_modified++; 1846 1956 thd0: 1847 1957 return dest; … … 1851 1961 // if dist <= 0, do not include, or go past, a NewLine 1852 1962 // 1853 static char *yank_delete(char * start, char *stop, int dist, int yf)1963 static char *yank_delete(char *start, char *stop, int dist, int yf) 1854 1964 { 1855 1965 char *p; … … 1895 2005 #endif 1896 2006 #if ENABLE_FEATURE_VI_YANKMARK 1897 "\n\tLine marking with 1898 "\n\tNamed buffers with 2007 "\n\tLine marking with 'x" 2008 "\n\tNamed buffers with \"x" 1899 2009 #endif 1900 2010 #if ENABLE_FEATURE_VI_READONLY … … 1918 2028 } 1919 2029 1920 static inline void print_literal(char * buf, const char * s) // copy s to buf, convert unprintable1921 {1922 unsigned char c;1923 char b[2];1924 1925 b[1] = '\0';1926 buf[0] = '\0';1927 if (!s[0])1928 s = "(NULL)";1929 for (; *s; s++) {1930 int c_is_no_print;1931 1932 c = *s;1933 c_is_no_print = (c & 0x80) && !Isprint(c);1934 if (c_is_no_print) {1935 strcat(buf, SOn);1936 c = '.';1937 }1938 if (c < ' ' || c == 127) {1939 strcat(buf, "^");1940 if (c == 127)1941 c = '?';1942 else1943 c += '@';1944 }1945 b[0] = c;1946 strcat(buf, b);1947 if (c_is_no_print)1948 strcat(buf, SOs);1949 if (*s == '\n')1950 strcat(buf, "$");1951 }1952 }1953 1954 2030 #if ENABLE_FEATURE_VI_DOT_CMD 1955 2031 static void start_new_cmd_q(char c) 1956 2032 { 1957 // release old cmd1958 free(last_modifying_cmd);1959 2033 // get buffer for new cmd 1960 last_modifying_cmd = xzalloc(MAX_LINELEN);1961 2034 // if there is a current cmd count put it in the buffer first 1962 if (cmdcnt > 0) 1963 sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);1964 else// just save char c onto queue2035 if (cmdcnt > 0) { 2036 lmc_len = sprintf(last_modifying_cmd, "%d%c", cmdcnt, c); 2037 } else { // just save char c onto queue 1965 2038 last_modifying_cmd[0] = c; 2039 lmc_len = 1; 2040 } 1966 2041 adding2q = 1; 1967 2042 } … … 1979 2054 || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \ 1980 2055 || ENABLE_FEATURE_VI_CRASHME 1981 static char *string_insert(char * p, char * s) // insert the string at 'p' 1982 { 1983 int cnt, i; 2056 // might reallocate text[]! use p += string_insert(p, ...), 2057 // and be careful to not use pointers into potentially freed text[]! 2058 static uintptr_t string_insert(char *p, const char *s) // insert the string at 'p' 2059 { 2060 uintptr_t bias; 2061 int i; 1984 2062 1985 2063 i = strlen(s); 1986 if (text_hole_make(p, i)) { 1987 strncpy(p, s, i); 2064 bias = text_hole_make(p, i); 2065 p += bias; 2066 memcpy(p, s, i); 2067 #if ENABLE_FEATURE_VI_YANKMARK 2068 { 2069 int cnt; 1988 2070 for (cnt = 0; *s != '\0'; s++) { 1989 2071 if (*s == '\n') 1990 2072 cnt++; 1991 2073 } 2074 status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg()); 2075 } 2076 #endif 2077 return bias; 2078 } 2079 #endif 2080 1992 2081 #if ENABLE_FEATURE_VI_YANKMARK 1993 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg()); 1994 #endif 1995 } 1996 return p; 1997 } 1998 #endif 1999 2000 #if ENABLE_FEATURE_VI_YANKMARK 2001 static char *text_yank(char * p, char * q, int dest) // copy text into a register 2002 { 2003 char *t; 2004 int cnt; 2005 2006 if (q < p) { // they are backwards- reverse them 2007 t = q; 2008 q = p; 2009 p = t; 2010 } 2011 cnt = q - p + 1; 2012 t = reg[dest]; 2013 free(t); // if already a yank register, free it 2014 t = xmalloc(cnt + 1); // get a new register 2015 memset(t, '\0', cnt + 1); // clear new text[] 2016 strncpy(t, p, cnt); // copy text[] into bufer 2017 reg[dest] = t; 2082 static char *text_yank(char *p, char *q, int dest) // copy text into a register 2083 { 2084 int cnt = q - p; 2085 if (cnt < 0) { // they are backwards- reverse them 2086 p = q; 2087 cnt = -cnt; 2088 } 2089 free(reg[dest]); // if already a yank register, free it 2090 reg[dest] = xstrndup(p, cnt + 1); 2018 2091 return p; 2019 2092 } … … 2050 2123 } 2051 2124 2052 static inline char *swap_context(char *p) // goto new context for '' command make this the current context2125 static char *swap_context(char *p) // goto new context for '' command make this the current context 2053 2126 { 2054 2127 char *tmp; … … 2080 2153 term_vi.c_cc[VTIME] = 0; 2081 2154 erase_char = term_vi.c_cc[VERASE]; 2082 tcsetattr (0, TCSANOW,&term_vi);2155 tcsetattr_stdin_TCSANOW(&term_vi); 2083 2156 } 2084 2157 2085 2158 static void cookmode(void) 2086 2159 { 2087 fflush(stdout); 2088 tcsetattr(0, TCSANOW, &term_orig); 2089 } 2090 2160 fflush_all(); 2161 tcsetattr_stdin_TCSANOW(&term_orig); 2162 } 2163 2164 #if ENABLE_FEATURE_VI_USE_SIGNALS 2091 2165 //----- Come here when we get a window resize signal --------- 2092 #if ENABLE_FEATURE_VI_USE_SIGNALS 2093 static void winch_sig(int sig ATTRIBUTE_UNUSED) 2094 { 2166 static void winch_sig(int sig UNUSED_PARAM) 2167 { 2168 int save_errno = errno; 2169 // FIXME: do it in main loop!!! 2095 2170 signal(SIGWINCH, winch_sig); 2096 if (ENABLE_FEATURE_VI_WIN_RESIZE) 2097 get_terminal_width_height(0, &columns, &rows); 2171 query_screen_dimensions(); 2098 2172 new_screen(rows, columns); // get memory for virtual screen 2099 2173 redraw(TRUE); // re-draw the screen 2174 errno = save_errno; 2100 2175 } 2101 2176 2102 2177 //----- Come here when we get a continue signal ------------------- 2103 static void cont_sig(int sig ATTRIBUTE_UNUSED) 2104 { 2105 rawmode(); // terminal to "raw" 2106 last_status_cksum = 0; // force status update 2107 redraw(TRUE); // re-draw the screen 2178 static void cont_sig(int sig UNUSED_PARAM) 2179 { 2180 int save_errno = errno; 2181 rawmode(); // terminal to "raw" 2182 last_status_cksum = 0; // force status update 2183 redraw(TRUE); // re-draw the screen 2108 2184 2109 2185 signal(SIGTSTP, suspend_sig); 2110 2186 signal(SIGCONT, SIG_DFL); 2111 kill(my_pid, SIGCONT); 2187 //kill(my_pid, SIGCONT); // huh? why? we are already "continued"... 2188 errno = save_errno; 2112 2189 } 2113 2190 2114 2191 //----- Come here when we get a Suspend signal ------------------- 2115 static void suspend_sig(int sig ATTRIBUTE_UNUSED)2116 { 2117 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen2118 clear_to_eol(); // Erase to end of line2119 cookmode(); 2192 static void suspend_sig(int sig UNUSED_PARAM) 2193 { 2194 int save_errno = errno; 2195 go_bottom_and_clear_to_eol(); 2196 cookmode(); // terminal to "cooked" 2120 2197 2121 2198 signal(SIGCONT, cont_sig); 2122 2199 signal(SIGTSTP, SIG_DFL); 2123 2200 kill(my_pid, SIGTSTP); 2201 errno = save_errno; 2124 2202 } 2125 2203 … … 2128 2206 { 2129 2207 signal(SIGINT, catch_sig); 2130 if (sig) 2131 longjmp(restart, sig); 2208 siglongjmp(restart, sig); 2132 2209 } 2133 2210 #endif /* FEATURE_VI_USE_SIGNALS */ 2134 2211 2135 static int mysleep(int hund) // sleep for 'h' 1/100 seconds 2136 { 2137 fd_set rfds; 2138 struct timeval tv; 2139 2140 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000 2141 fflush(stdout); 2142 FD_ZERO(&rfds); 2143 FD_SET(0, &rfds); 2144 tv.tv_sec = 0; 2145 tv.tv_usec = hund * 10000; 2146 select(1, &rfds, NULL, NULL, &tv); 2147 return FD_ISSET(0, &rfds); 2148 } 2149 2150 #define readbuffer bb_common_bufsiz1 2151 2152 static int readed_for_parse; 2212 static int mysleep(int hund) // sleep for 'hund' 1/100 seconds or stdin ready 2213 { 2214 struct pollfd pfd[1]; 2215 2216 pfd[0].fd = STDIN_FILENO; 2217 pfd[0].events = POLLIN; 2218 return safe_poll(pfd, 1, hund*10) > 0; 2219 } 2153 2220 2154 2221 //----- IO Routines -------------------------------------------- 2155 static char readit(void) // read (maybe cursor) key from stdin 2156 { 2157 char c; 2158 int n; 2159 struct esc_cmds { 2160 const char *seq; 2161 char val; 2162 }; 2163 2164 static const struct esc_cmds esccmds[] = { 2165 {"OA", VI_K_UP}, // cursor key Up 2166 {"OB", VI_K_DOWN}, // cursor key Down 2167 {"OC", VI_K_RIGHT}, // Cursor Key Right 2168 {"OD", VI_K_LEFT}, // cursor key Left 2169 {"OH", VI_K_HOME}, // Cursor Key Home 2170 {"OF", VI_K_END}, // Cursor Key End 2171 {"[A", VI_K_UP}, // cursor key Up 2172 {"[B", VI_K_DOWN}, // cursor key Down 2173 {"[C", VI_K_RIGHT}, // Cursor Key Right 2174 {"[D", VI_K_LEFT}, // cursor key Left 2175 {"[H", VI_K_HOME}, // Cursor Key Home 2176 {"[F", VI_K_END}, // Cursor Key End 2177 {"[1~", VI_K_HOME}, // Cursor Key Home 2178 {"[2~", VI_K_INSERT}, // Cursor Key Insert 2179 {"[4~", VI_K_END}, // Cursor Key End 2180 {"[5~", VI_K_PAGEUP}, // Cursor Key Page Up 2181 {"[6~", VI_K_PAGEDOWN},// Cursor Key Page Down 2182 {"OP", VI_K_FUN1}, // Function Key F1 2183 {"OQ", VI_K_FUN2}, // Function Key F2 2184 {"OR", VI_K_FUN3}, // Function Key F3 2185 {"OS", VI_K_FUN4}, // Function Key F4 2186 {"[15~", VI_K_FUN5}, // Function Key F5 2187 {"[17~", VI_K_FUN6}, // Function Key F6 2188 {"[18~", VI_K_FUN7}, // Function Key F7 2189 {"[19~", VI_K_FUN8}, // Function Key F8 2190 {"[20~", VI_K_FUN9}, // Function Key F9 2191 {"[21~", VI_K_FUN10}, // Function Key F10 2192 {"[23~", VI_K_FUN11}, // Function Key F11 2193 {"[24~", VI_K_FUN12}, // Function Key F12 2194 {"[11~", VI_K_FUN1}, // Function Key F1 2195 {"[12~", VI_K_FUN2}, // Function Key F2 2196 {"[13~", VI_K_FUN3}, // Function Key F3 2197 {"[14~", VI_K_FUN4}, // Function Key F4 2198 }; 2199 enum { ESCCMDS_COUNT = ARRAY_SIZE(esccmds) }; 2200 2201 alarm(0); // turn alarm OFF while we wait for input 2202 fflush(stdout); 2203 n = readed_for_parse; 2204 // get input from User- are there already input chars in Q? 2205 if (n <= 0) { 2206 ri0: 2207 // the Q is empty, wait for a typed char 2208 n = read(0, readbuffer, MAX_LINELEN - 1); 2209 if (n < 0) { 2210 if (errno == EINTR) 2211 goto ri0; // interrupted sys call 2212 if (errno == EBADF || errno == EFAULT || errno == EINVAL 2213 || errno == EIO) 2214 editing = 0; 2215 errno = 0; 2216 } 2217 if (n <= 0) 2218 return 0; // error 2219 if (readbuffer[0] == 27) { 2220 fd_set rfds; 2221 struct timeval tv; 2222 2223 // This is an ESC char. Is this Esc sequence? 2224 // Could be bare Esc key. See if there are any 2225 // more chars to read after the ESC. This would 2226 // be a Function or Cursor Key sequence. 2227 FD_ZERO(&rfds); 2228 FD_SET(0, &rfds); 2229 tv.tv_sec = 0; 2230 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000 2231 2232 // keep reading while there are input chars and room in buffer 2233 while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (MAX_LINELEN - 5)) { 2234 // read the rest of the ESC string 2235 int r = read(0, (void *) (readbuffer + n), MAX_LINELEN - n); 2236 if (r > 0) { 2237 n += r; 2238 } 2239 } 2240 } 2241 readed_for_parse = n; 2242 } 2243 c = readbuffer[0]; 2244 if (c == 27 && n > 1) { 2245 // Maybe cursor or function key? 2246 const struct esc_cmds *eindex; 2247 2248 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) { 2249 int cnt = strlen(eindex->seq); 2250 2251 if (n <= cnt) 2252 continue; 2253 if (strncmp(eindex->seq, readbuffer + 1, cnt)) 2254 continue; 2255 // is a Cursor key- put derived value back into Q 2256 c = eindex->val; 2257 // for squeeze out the ESC sequence 2258 n = cnt + 1; 2259 break; 2260 } 2261 if (eindex == &esccmds[ESCCMDS_COUNT]) { 2262 /* defined ESC sequence not found, set only one ESC */ 2263 n = 1; 2264 } 2265 } else { 2266 n = 1; 2267 } 2268 // remove key sequence from Q 2269 readed_for_parse -= n; 2270 memmove(readbuffer, readbuffer + n, MAX_LINELEN - n); 2271 alarm(3); // we are done waiting for input, turn alarm ON 2222 static int readit(void) // read (maybe cursor) key from stdin 2223 { 2224 int c; 2225 2226 fflush_all(); 2227 c = read_key(STDIN_FILENO, readbuffer, /*timeout off:*/ -2); 2228 if (c == -1) { // EOF/error 2229 go_bottom_and_clear_to_eol(); 2230 cookmode(); // terminal to "cooked" 2231 bb_error_msg_and_die("can't read user input"); 2232 } 2272 2233 return c; 2273 2234 } 2274 2235 2275 2236 //----- IO Routines -------------------------------------------- 2276 static charget_one_char(void)2277 { 2278 static charc;2237 static int get_one_char(void) 2238 { 2239 int c; 2279 2240 2280 2241 #if ENABLE_FEATURE_VI_DOT_CMD 2281 // ! adding2q && ioq == 0 read()2282 // ! adding2q && ioq != 0 *ioq2283 // adding2q *last_modifying_cmd= read()2284 2242 if (!adding2q) { 2285 2243 // we are not adding to the q. … … 2290 2248 } else { 2291 2249 // there is a queue to get chars from first 2292 c = *ioq++; 2250 // careful with correct sign expansion! 2251 c = (unsigned char)*ioq++; 2293 2252 if (c == '\0') { 2294 2253 // the end of the q, read from STDIN … … 2301 2260 // adding STDIN chars to q 2302 2261 c = readit(); // get the users input 2303 if (last_modifying_cmd != 0) { 2304 int len = strlen(last_modifying_cmd); 2305 if (len >= MAX_LINELEN - 1) { 2306 psbs("last_modifying_cmd overrun"); 2307 } else { 2308 // add new char to q 2309 last_modifying_cmd[len] = c; 2310 } 2262 if (lmc_len >= MAX_INPUT_LEN - 1) { 2263 status_line_bold("last_modifying_cmd overrun"); 2264 } else { 2265 // add new char to q 2266 last_modifying_cmd[lmc_len++] = c; 2311 2267 } 2312 2268 } … … 2314 2270 c = readit(); // get the users input 2315 2271 #endif /* FEATURE_VI_DOT_CMD */ 2316 return c; // return the char, where ever it came from 2317 } 2318 2319 static char *get_input_line(const char * prompt) // get input line- use "status line" 2320 { 2321 static char *obufp; 2322 2323 char buf[MAX_LINELEN]; 2324 char c; 2272 return c; 2273 } 2274 2275 // Get input line (uses "status line" area) 2276 static char *get_input_line(const char *prompt) 2277 { 2278 // char [MAX_INPUT_LEN] 2279 #define buf get_input_line__buf 2280 2281 int c; 2325 2282 int i; 2326 2283 2327 2284 strcpy(buf, prompt); 2328 2285 last_status_cksum = 0; // force status update 2329 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen 2330 clear_to_eol(); // clear the line 2286 go_bottom_and_clear_to_eol(); 2331 2287 write1(prompt); // write out the :, /, or ? prompt 2332 2288 2333 2289 i = strlen(buf); 2334 while (i < MAX_ LINELEN) {2335 c = get_one_char(); // read user input2290 while (i < MAX_INPUT_LEN) { 2291 c = get_one_char(); 2336 2292 if (c == '\n' || c == '\r' || c == 27) 2337 break; // is this end of input2293 break; // this is end of input 2338 2294 if (c == erase_char || c == 8 || c == 127) { 2339 2295 // user wants to erase prev char 2340 i--; // backup to prev char 2341 buf[i] = '\0'; // erase the char 2342 buf[i + 1] = '\0'; // null terminate buffer 2343 write1("\b \b"); // erase char on screen 2344 if (i <= 0) { // user backs up before b-o-l, exit 2296 buf[--i] = '\0'; 2297 write1("\b \b"); // erase char on screen 2298 if (i <= 0) // user backs up before b-o-l, exit 2345 2299 break; 2346 } 2347 } else { 2348 buf[i] = c; // save char in buffer 2349 buf[i + 1] = '\0'; // make sure buffer is null terminated 2350 putchar(c); // echo the char back to user 2351 i++; 2300 } else if (c > 0 && c < 256) { // exclude Unicode 2301 // (TODO: need to handle Unicode) 2302 buf[i] = c; 2303 buf[++i] = '\0'; 2304 bb_putchar(c); 2352 2305 } 2353 2306 } 2354 2307 refresh(FALSE); 2355 free(obufp); 2356 obufp = xstrdup(buf); 2357 return obufp; 2308 return buf; 2309 #undef buf 2358 2310 } 2359 2311 … … 2364 2316 2365 2317 cnt = -1; 2366 if (fn && fn[0] &&stat(fn, &st_buf) == 0) // see if file exists2318 if (fn && stat(fn, &st_buf) == 0) // see if file exists 2367 2319 cnt = (int) st_buf.st_size; 2368 2320 return cnt; 2369 2321 } 2370 2322 2371 static int file_insert(const char * fn, char *p 2372 USE_FEATURE_VI_READONLY(, int update_ro_status))2323 // might reallocate text[]! 2324 static int file_insert(const char *fn, char *p, int update_ro_status) 2373 2325 { 2374 2326 int cnt = -1; … … 2378 2330 /* Validate file */ 2379 2331 if (stat(fn, &statbuf) < 0) { 2380 psbs("\"%s\" %s", fn, strerror(errno));2332 status_line_bold("\"%s\" %s", fn, strerror(errno)); 2381 2333 goto fi0; 2382 2334 } 2383 if ( (statbuf.st_mode & S_IFREG) == 0) {2335 if (!S_ISREG(statbuf.st_mode)) { 2384 2336 // This is not a regular file 2385 psbs("\"%s\" Not a regular file", fn);2337 status_line_bold("\"%s\" Not a regular file", fn); 2386 2338 goto fi0; 2387 2339 } 2388 /* // this check is done by open()2389 if ((statbuf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {2390 // dont have any read permissions2391 psbs("\"%s\" Not readable", fn);2392 goto fi0;2393 }2394 */2395 2340 if (p < text || p > end) { 2396 psbs("Trying to insert file outside of memory");2341 status_line_bold("Trying to insert file outside of memory"); 2397 2342 goto fi0; 2398 2343 } … … 2401 2346 fd = open(fn, O_RDONLY); 2402 2347 if (fd < 0) { 2403 psbs("\"%s\" %s", fn, strerror(errno));2348 status_line_bold("\"%s\" %s", fn, strerror(errno)); 2404 2349 goto fi0; 2405 2350 } 2406 2351 size = statbuf.st_size; 2407 p = text_hole_make(p, size); 2408 if (p == NULL) 2409 goto fi0; 2410 cnt = read(fd, p, size); 2352 p += text_hole_make(p, size); 2353 cnt = safe_read(fd, p, size); 2411 2354 if (cnt < 0) { 2412 psbs("\"%s\" %s", fn, strerror(errno));2355 status_line_bold("\"%s\" %s", fn, strerror(errno)); 2413 2356 p = text_hole_delete(p, p + size - 1); // un-do buffer insert 2414 2357 } else if (cnt < size) { 2415 2358 // There was a partial read, shrink unused space text[] 2416 2359 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert 2417 psbs("cannot read all of file \"%s\"", fn);2360 status_line_bold("can't read all of file \"%s\"", fn); 2418 2361 } 2419 2362 if (cnt >= size) … … 2435 2378 } 2436 2379 2437 2438 static int file_write(char * fn, char * first, char * last) 2380 static int file_write(char *fn, char *first, char *last) 2439 2381 { 2440 2382 int fd, cnt, charcnt; 2441 2383 2442 2384 if (fn == 0) { 2443 psbs("No current filename");2385 status_line_bold("No current filename"); 2444 2386 return -2; 2445 2387 } 2446 charcnt = 0; 2447 // FIXIT- use the correct umask() 2448 fd = open(fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664); 2388 /* By popular request we do not open file with O_TRUNC, 2389 * but instead ftruncate() it _after_ successful write. 2390 * Might reduce amount of data lost on power fail etc. 2391 */ 2392 fd = open(fn, (O_WRONLY | O_CREAT), 0666); 2449 2393 if (fd < 0) 2450 2394 return -1; 2451 2395 cnt = last - first + 1; 2452 charcnt = write(fd, first, cnt); 2396 charcnt = full_write(fd, first, cnt); 2397 ftruncate(fd, charcnt); 2453 2398 if (charcnt == cnt) { 2454 2399 // good write 2455 //file_modified = FALSE; // the file has not been modified2400 //file_modified = FALSE; 2456 2401 } else { 2457 2402 charcnt = 0; … … 2470 2415 // . ... . 2471 2416 // 22,0 ... 22,79 2472 // 23,0 ... 23,79 status line 2473 // 2417 // 23,0 ... 23,79 <- status line 2474 2418 2475 2419 //----- Move the cursor to row x col (count from 0, not 1) ------- 2476 static void place_cursor(int row, int col, int opti) 2477 { 2478 char cm1[MAX_LINELEN]; 2420 static void place_cursor(int row, int col, int optimize) 2421 { 2422 char cm1[sizeof(CMrc) + sizeof(int)*3 * 2]; 2423 #if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR 2424 enum { 2425 SZ_UP = sizeof(CMup), 2426 SZ_DN = sizeof(CMdown), 2427 SEQ_SIZE = SZ_UP > SZ_DN ? SZ_UP : SZ_DN, 2428 }; 2429 char cm2[SEQ_SIZE * 5 + 32]; // bigger than worst case size 2430 #endif 2479 2431 char *cm; 2480 #if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR2481 char cm2[MAX_LINELEN];2482 char *screenp;2483 // char cm3[MAX_LINELEN];2484 int Rrow = last_row;2485 #endif2486 2487 memset(cm1, '\0', MAX_LINELEN); // clear the buffer2488 2432 2489 2433 if (row < 0) row = 0; … … 2495 2439 sprintf(cm1, CMrc, row + 1, col + 1); 2496 2440 cm = cm1; 2497 if (!opti)2498 goto pc0;2499 2441 2500 2442 #if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR 2501 //----- find the minimum # of chars to move cursor ------------- 2502 //----- 2. Try moving with discreet chars (Newline, [back]space, ...) 2503 memset(cm2, '\0', MAX_LINELEN); // clear the buffer 2504 2505 // move to the correct row 2506 while (row < Rrow) { 2507 // the cursor has to move up 2508 strcat(cm2, CMup); 2509 Rrow--; 2510 } 2511 while (row > Rrow) { 2512 // the cursor has to move down 2513 strcat(cm2, CMdown); 2514 Rrow++; 2515 } 2516 2517 // now move to the correct column 2518 strcat(cm2, "\r"); // start at col 0 2519 // just send out orignal source char to get to correct place 2520 screenp = &screen[row * columns]; // start of screen line 2521 strncat(cm2, screenp, col); 2522 2523 //----- 3. Try some other way of moving cursor 2524 //--------------------------------------------- 2525 2526 // pick the shortest cursor motion to send out 2527 cm = cm1; 2528 if (strlen(cm2) < strlen(cm)) { 2529 cm = cm2; 2530 } /* else if (strlen(cm3) < strlen(cm)) { 2531 cm= cm3; 2532 } */ 2443 if (optimize && col < 16) { 2444 char *screenp; 2445 int Rrow = last_row; 2446 int diff = Rrow - row; 2447 2448 if (diff < -5 || diff > 5) 2449 goto skip; 2450 2451 //----- find the minimum # of chars to move cursor ------------- 2452 //----- 2. Try moving with discreet chars (Newline, [back]space, ...) 2453 cm2[0] = '\0'; 2454 2455 // move to the correct row 2456 while (row < Rrow) { 2457 // the cursor has to move up 2458 strcat(cm2, CMup); 2459 Rrow--; 2460 } 2461 while (row > Rrow) { 2462 // the cursor has to move down 2463 strcat(cm2, CMdown); 2464 Rrow++; 2465 } 2466 2467 // now move to the correct column 2468 strcat(cm2, "\r"); // start at col 0 2469 // just send out orignal source char to get to correct place 2470 screenp = &screen[row * columns]; // start of screen line 2471 strncat(cm2, screenp, col); 2472 2473 // pick the shortest cursor motion to send out 2474 if (strlen(cm2) < strlen(cm)) { 2475 cm = cm2; 2476 } 2477 skip: ; 2478 } 2479 last_row = row; 2533 2480 #endif /* FEATURE_VI_OPTIMIZE_CURSOR */ 2534 pc0: 2535 write1(cm); // move the cursor 2481 write1(cm); 2536 2482 } 2537 2483 … … 2540 2486 { 2541 2487 write1(Ceol); // Erase from cursor to end of line 2488 } 2489 2490 static void go_bottom_and_clear_to_eol(void) 2491 { 2492 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen 2493 clear_to_eol(); // erase to end of line 2542 2494 } 2543 2495 … … 2612 2564 } 2613 2565 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) { 2614 last_status_cksum = cksum; // remember if we have seen this line2615 place_cursor(rows - 1, 0, FALSE); // put cursor on status line2566 last_status_cksum = cksum; // remember if we have seen this line 2567 go_bottom_and_clear_to_eol(); 2616 2568 write1(status_buffer); 2617 clear_to_eol();2618 2569 if (have_status_msg) { 2619 2570 if (((int)strlen(status_buffer) - (have_status_msg - 1)) > … … 2626 2577 place_cursor(crow, ccol, FALSE); // put cursor back in correct place 2627 2578 } 2628 fflush (stdout);2579 fflush_all(); 2629 2580 } 2630 2581 2631 2582 //----- format the status buffer, the bottom line of screen ------ 2632 2583 // format status buffer, with STANDOUT mode 2633 static void psbs(const char *format, ...)2584 static void status_line_bold(const char *format, ...) 2634 2585 { 2635 2586 va_list args; … … 2637 2588 va_start(args, format); 2638 2589 strcpy(status_buffer, SOs); // Terminal standout mode on 2639 vsprintf(status_buffer + s trlen(status_buffer), format, args);2590 vsprintf(status_buffer + sizeof(SOs)-1, format, args); 2640 2591 strcat(status_buffer, SOn); // Terminal standout mode off 2641 2592 va_end(args); … … 2645 2596 2646 2597 // format status buffer 2647 static void psb(const char *format, ...)2598 static void status_line(const char *format, ...) 2648 2599 { 2649 2600 va_list args; … … 2656 2607 } 2657 2608 2658 static void ni(const char * s) // display messages 2659 { 2660 char buf[MAX_LINELEN]; 2609 // copy s to buf, convert unprintable 2610 static void print_literal(char *buf, const char *s) 2611 { 2612 char *d; 2613 unsigned char c; 2614 2615 buf[0] = '\0'; 2616 if (!s[0]) 2617 s = "(NULL)"; 2618 2619 d = buf; 2620 for (; *s; s++) { 2621 int c_is_no_print; 2622 2623 c = *s; 2624 c_is_no_print = (c & 0x80) && !Isprint(c); 2625 if (c_is_no_print) { 2626 strcpy(d, SOn); 2627 d += sizeof(SOn)-1; 2628 c = '.'; 2629 } 2630 if (c < ' ' || c == 0x7f) { 2631 *d++ = '^'; 2632 c |= '@'; /* 0x40 */ 2633 if (c == 0x7f) 2634 c = '?'; 2635 } 2636 *d++ = c; 2637 *d = '\0'; 2638 if (c_is_no_print) { 2639 strcpy(d, SOs); 2640 d += sizeof(SOs)-1; 2641 } 2642 if (*s == '\n') { 2643 *d++ = '$'; 2644 *d = '\0'; 2645 } 2646 if (d - buf > MAX_INPUT_LEN - 10) // paranoia 2647 break; 2648 } 2649 } 2650 2651 static void not_implemented(const char *s) 2652 { 2653 char buf[MAX_INPUT_LEN]; 2661 2654 2662 2655 print_literal(buf, s); 2663 psbs("\'%s\' is not implemented", buf);2664 } 2665 2666 static int format_edit_status(void)// show file status on status line2667 { 2668 static int tot; 2656 status_line_bold("\'%s\' is not implemented", buf); 2657 } 2658 2659 // show file status on status line 2660 static int format_edit_status(void) 2661 { 2669 2662 static const char cmd_mode_indicator[] ALIGN1 = "-IR-"; 2663 2664 #define tot format_edit_status__tot 2665 2670 2666 int cur, percent, ret, trunc_at; 2671 2667 … … 2718 2714 2719 2715 return trunc_at; /* had to truncate */ 2716 #undef tot 2720 2717 } 2721 2718 … … 2724 2721 { 2725 2722 place_cursor(0, 0, FALSE); // put cursor in correct place 2726 clear_to_eos(); // tel terminal to erase display2723 clear_to_eos(); // tell terminal to erase display 2727 2724 screen_erase(); // erase the internal screen buffer 2728 2725 last_status_cksum = 0; // force status update … … 2732 2729 2733 2730 //----- Format a text[] line into a buffer --------------------- 2734 static void format_line(char *dest, char *src, int li) 2735 { 2731 static char* format_line(char *src /*, int li*/) 2732 { 2733 unsigned char c; 2736 2734 int co; 2737 char c; 2738 2739 for (co = 0; co < MAX_SCR_COLS; co++) { 2740 c = ' '; // assume blank 2741 if (li > 0 && co == 0) { 2742 c = '~'; // not first line, assume Tilde 2743 } 2744 // are there chars in text[] and have we gone past the end 2745 if (text < end && src < end) { 2735 int ofs = offset; 2736 char *dest = scr_out_buf; // [MAX_SCR_COLS + MAX_TABSTOP * 2] 2737 2738 c = '~'; // char in col 0 in non-existent lines is '~' 2739 co = 0; 2740 while (co < columns + tabstop) { 2741 // have we gone past the end? 2742 if (src < end) { 2746 2743 c = *src++; 2747 } 2748 if (c == '\n') 2749 break; 2750 if ((c & 0x80) && !Isprint(c)) { 2751 c = '.'; 2752 } 2753 if ((unsigned char)(c) < ' ' || c == 0x7f) { 2754 if (c == '\t') { 2755 c = ' '; 2756 // co % 8 != 7 2757 for (; (co % tabstop) != (tabstop - 1); co++) { 2758 dest[co] = c; 2744 if (c == '\n') 2745 break; 2746 if ((c & 0x80) && !Isprint(c)) { 2747 c = '.'; 2748 } 2749 if (c < ' ' || c == 0x7f) { 2750 if (c == '\t') { 2751 c = ' '; 2752 // co % 8 != 7 2753 while ((co % tabstop) != (tabstop - 1)) { 2754 dest[co++] = c; 2755 } 2756 } else { 2757 dest[co++] = '^'; 2758 if (c == 0x7f) 2759 c = '?'; 2760 else 2761 c += '@'; // Ctrl-X -> 'X' 2759 2762 } 2760 } else {2761 dest[co++] = '^';2762 if (c == 0x7f)2763 c = '?';2764 else2765 c += '@'; // make it visible2766 2763 } 2767 2764 } 2768 // the co++ is done here so that the column will 2769 // not be overwritten when we blank-out the rest of line 2770 dest[co] = c; 2765 dest[co++] = c; 2766 // discard scrolled-off-to-the-left portion, 2767 // in tabstop-sized pieces 2768 if (ofs >= tabstop && co >= tabstop) { 2769 memmove(dest, dest + tabstop, co); 2770 co -= tabstop; 2771 ofs -= tabstop; 2772 } 2771 2773 if (src >= end) 2772 2774 break; 2773 2775 } 2776 // check "short line, gigantic offset" case 2777 if (co < ofs) 2778 ofs = co; 2779 // discard last scrolled off part 2780 co -= ofs; 2781 dest += ofs; 2782 // fill the rest with spaces 2783 if (co < columns) 2784 memset(&dest[co], ' ', columns - co); 2785 return dest; 2774 2786 } 2775 2787 … … 2781 2793 static void refresh(int full_screen) 2782 2794 { 2783 static int old_offset; 2795 #define old_offset refresh__old_offset 2784 2796 2785 2797 int li, changed; 2786 char buf[MAX_SCR_COLS];2787 2798 char *tp, *sp; // pointer into text[] and screen[] 2788 #if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR 2789 i nt last_li = -2; // last line that changed- for optimizing cursor movement2790 #endif 2791 2792 if (ENABLE_FEATURE_VI_WIN_RESIZE)2793 get_terminal_width_height(0, &columns, &rows);2799 2800 if (ENABLE_FEATURE_VI_WIN_RESIZE IF_FEATURE_VI_ASK_TERMINAL(&& !G.get_rowcol_error) ) { 2801 unsigned c = columns, r = rows; 2802 query_screen_dimensions(); 2803 full_screen |= (c - columns) | (r - rows); 2804 } 2794 2805 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot") 2795 2806 tp = screenbegin; // index into text[] of top line … … 2798 2809 for (li = 0; li < rows - 1; li++) { 2799 2810 int cs, ce; // column start & end 2800 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer 2801 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer 2802 // format current text line into buf 2803 format_line(buf, tp, li); 2811 char *out_buf; 2812 // format current text line 2813 out_buf = format_line(tp /*, li*/); 2804 2814 2805 2815 // skip to the end of the current text[] line 2806 while (tp < end && *tp++ != '\n') /*no-op*/; 2807 2808 // see if there are any changes between vitual screen and buf 2816 if (tp < end) { 2817 char *t = memchr(tp, '\n', end - tp); 2818 if (!t) t = end - 1; 2819 tp = t + 1; 2820 } 2821 2822 // see if there are any changes between vitual screen and out_buf 2809 2823 changed = FALSE; // assume no change 2810 cs = 0;2811 ce = columns-1;2824 cs = 0; 2825 ce = columns - 1; 2812 2826 sp = &screen[li * columns]; // start of screen line 2813 2827 if (full_screen) { … … 2818 2832 // look forward for first difference between buf and screen 2819 2833 for (; cs <= ce; cs++) { 2820 if ( buf[cs + offset] != sp[cs]) {2834 if (out_buf[cs] != sp[cs]) { 2821 2835 changed = TRUE; // mark for redraw 2822 2836 break; … … 2824 2838 } 2825 2839 2826 // look backward for last difference between buf and screen2827 for ( 2828 if ( buf[ce + offset] != sp[ce]) {2840 // look backward for last difference between out_buf and screen 2841 for (; ce >= cs; ce--) { 2842 if (out_buf[ce] != sp[ce]) { 2829 2843 changed = TRUE; // mark for redraw 2830 2844 break; … … 2840 2854 2841 2855 // make a sanity check of columns indexes 2842 if (cs < 0) cs = 0;2843 if (ce > columns -1) ce= columns-1;2844 if (cs > ce) { cs= 0; ce= columns-1;}2845 // is there a change between vitual screen and buf2856 if (cs < 0) cs = 0; 2857 if (ce > columns - 1) ce = columns - 1; 2858 if (cs > ce) { cs = 0; ce = columns - 1; } 2859 // is there a change between vitual screen and out_buf 2846 2860 if (changed) { 2847 // 2848 mem move(sp+cs, buf+(cs+offset), ce-cs+1);2861 // copy changed part of buffer to virtual screen 2862 memcpy(sp+cs, out_buf+cs, ce-cs+1); 2849 2863 2850 2864 // move cursor to column of first change 2851 if (offset != old_offset) { 2852 // opti_cur_move is still too stupid 2853 // to handle offsets correctly 2854 place_cursor(li, cs, FALSE); 2855 } else { 2856 #if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR 2857 // if this just the next line 2858 // try to optimize cursor movement 2859 // otherwise, use standard ESC sequence 2860 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE); 2861 last_li= li; 2862 #else 2863 place_cursor(li, cs, FALSE); // use standard ESC sequence 2864 #endif /* FEATURE_VI_OPTIMIZE_CURSOR */ 2865 } 2865 //if (offset != old_offset) { 2866 // // place_cursor is still too stupid 2867 // // to handle offsets correctly 2868 // place_cursor(li, cs, FALSE); 2869 //} else { 2870 place_cursor(li, cs, TRUE); 2871 //} 2866 2872 2867 2873 // write line out to terminal 2868 { 2869 int nic = ce - cs + 1; 2870 char *out = sp + cs; 2871 2872 while (nic-- > 0) { 2873 putchar(*out); 2874 out++; 2875 } 2876 } 2877 #if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR 2878 last_row = li; 2879 #endif 2880 } 2881 } 2882 2883 #if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR 2884 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE); 2885 last_row = crow; 2886 #else 2887 place_cursor(crow, ccol, FALSE); 2888 #endif 2889 2890 if (offset != old_offset) 2891 old_offset = offset; 2874 fwrite(&sp[cs], ce - cs + 1, 1, stdout); 2875 } 2876 } 2877 2878 place_cursor(crow, ccol, TRUE); 2879 2880 old_offset = offset; 2881 #undef old_offset 2892 2882 } 2893 2883 … … 2914 2904 2915 2905 //----- Execute a Vi Command ----------------------------------- 2916 static void do_cmd(char c) 2917 { 2918 const char *msg; 2919 char c1, *p, *q, buf[9], *save_dot; 2920 int cnt, i, j, dir, yf; 2921 2922 c1 = c; // quiet the compiler 2923 cnt = yf = dir = 0; // quiet the compiler 2924 msg = p = q = save_dot = buf; // quiet the compiler 2925 memset(buf, '\0', 9); // clear buf 2906 static void do_cmd(int c) 2907 { 2908 const char *msg = msg; // for compiler 2909 char *p, *q, *save_dot; 2910 char buf[12]; 2911 int dir; 2912 int cnt, i, j; 2913 int c1; 2914 2915 // c1 = c; // quiet the compiler 2916 // cnt = yf = 0; // quiet the compiler 2917 // msg = p = q = save_dot = buf; // quiet the compiler 2918 memset(buf, '\0', 12); 2926 2919 2927 2920 show_status_line(); … … 2929 2922 /* if this is a cursor key, skip these checks */ 2930 2923 switch (c) { 2931 case VI_K_UP: 2932 case VI_K_DOWN: 2933 case VI_K_LEFT: 2934 case VI_K_RIGHT: 2935 case VI_K_HOME: 2936 case VI_K_END: 2937 case VI_K_PAGEUP: 2938 case VI_K_PAGEDOWN: 2924 case KEYCODE_UP: 2925 case KEYCODE_DOWN: 2926 case KEYCODE_LEFT: 2927 case KEYCODE_RIGHT: 2928 case KEYCODE_HOME: 2929 case KEYCODE_END: 2930 case KEYCODE_PAGEUP: 2931 case KEYCODE_PAGEDOWN: 2932 case KEYCODE_DELETE: 2939 2933 goto key_cmd_mode; 2940 2934 } … … 2942 2936 if (cmd_mode == 2) { 2943 2937 // flip-flop Insert/Replace mode 2944 if (c == VI_K_INSERT)2938 if (c == KEYCODE_INSERT) 2945 2939 goto dc_i; 2946 2940 // we are 'R'eplacing the current *dot with new char … … 2959 2953 if (cmd_mode == 1) { 2960 2954 // hitting "Insert" twice means "R" replace mode 2961 if (c == VI_K_INSERT) goto dc5;2955 if (c == KEYCODE_INSERT) goto dc5; 2962 2956 // insert the char c at "dot" 2963 2957 if (1 <= c || Isprint(c)) { … … 2995 2989 //case ')': // )- 2996 2990 //case '*': // *- 2997 //case ',': // ,-2998 2991 //case '=': // =- 2999 2992 //case '@': // @- … … 3009 3002 //case '_': // _- 3010 3003 //case '`': // `- 3011 //case 'g': // g-3012 3004 //case 'u': // u- FIXME- there is no undo 3013 3005 //case 'v': // v- 3014 default: // unrecogni sed command3006 default: // unrecognized command 3015 3007 buf[0] = c; 3016 3008 buf[1] = '\0'; 3017 if (c < ' ') { 3018 buf[0] = '^'; 3019 buf[1] = c + '@'; 3020 buf[2] = '\0'; 3021 } 3022 ni(buf); 3009 not_implemented(buf); 3023 3010 end_cmd_q(); // stop adding to q 3024 3011 case 0x00: // nul- ignore 3025 3012 break; 3026 3013 case 2: // ctrl-B scroll up full screen 3027 case VI_K_PAGEUP: // Cursor Key Page Up3014 case KEYCODE_PAGEUP: // Cursor Key Page Up 3028 3015 dot_scroll(rows - 2, -1); 3029 3016 break; 3030 #if ENABLE_FEATURE_VI_USE_SIGNALS3031 case 0x03: // ctrl-C interrupt3032 longjmp(restart, 1);3033 break;3034 case 26: // ctrl-Z suspend3035 suspend_sig(SIGTSTP);3036 break;3037 #endif3038 3017 case 4: // ctrl-D scroll down half screen 3039 3018 dot_scroll((rows - 2) / 2, 1); … … 3043 3022 break; 3044 3023 case 6: // ctrl-F scroll down full screen 3045 case VI_K_PAGEDOWN: // Cursor Key Page Down3024 case KEYCODE_PAGEDOWN: // Cursor Key Page Down 3046 3025 dot_scroll(rows - 2, 1); 3047 3026 break; … … 3050 3029 break; 3051 3030 case 'h': // h- move left 3052 case VI_K_LEFT: // cursor key Left3031 case KEYCODE_LEFT: // cursor key Left 3053 3032 case 8: // ctrl-H- move left (This may be ERASE char) 3054 3033 case 0x7f: // DEL- move left (This may be ERASE char) 3055 if ( cmdcnt-- > 1) {3034 if (--cmdcnt > 0) { 3056 3035 do_cmd(c); 3057 } // repeat cnt3036 } 3058 3037 dot_left(); 3059 3038 break; 3060 3039 case 10: // Newline ^J 3061 3040 case 'j': // j- goto next line, same col 3062 case VI_K_DOWN: // cursor key Down3063 if ( cmdcnt-- > 1) {3041 case KEYCODE_DOWN: // cursor key Down 3042 if (--cmdcnt > 0) { 3064 3043 do_cmd(c); 3065 } // repeat cnt3044 } 3066 3045 dot_next(); // go to next B-o-l 3067 3046 dot = move_to_col(dot, ccol + offset); // try stay in same col … … 3078 3057 case 13: // Carriage Return ^M 3079 3058 case '+': // +- goto next line 3080 if ( cmdcnt-- > 1) {3059 if (--cmdcnt > 0) { 3081 3060 do_cmd(c); 3082 } // repeat cnt3061 } 3083 3062 dot_next(); 3084 3063 dot_skip_over_ws(); … … 3099 3078 case ' ': // move right 3100 3079 case 'l': // move right 3101 case VI_K_RIGHT: // Cursor Key Right3102 if ( cmdcnt-- > 1) {3080 case KEYCODE_RIGHT: // Cursor Key Right 3081 if (--cmdcnt > 0) { 3103 3082 do_cmd(c); 3104 } // repeat cnt3083 } 3105 3084 dot_right(); 3106 3085 break; 3107 3086 #if ENABLE_FEATURE_VI_YANKMARK 3108 3087 case '"': // "- name a register to use for Delete/Yank 3109 c1 = get_one_char(); 3110 c1 = tolower(c1); 3111 if (islower(c1)) { 3112 YDreg = c1 - 'a'; 3088 c1 = (get_one_char() | 0x20) - 'a'; // | 0x20 is tolower() 3089 if ((unsigned)c1 <= 25) { // a-z? 3090 YDreg = c1; 3113 3091 } else { 3114 3092 indicate_error(c); … … 3116 3094 break; 3117 3095 case '\'': // '- goto a specific mark 3118 c1 = get_one_char(); 3119 c1 = tolower(c1); 3120 if (islower(c1)) { 3121 c1 = c1 - 'a'; 3096 c1 = (get_one_char() | 0x20) - 'a'; 3097 if ((unsigned)c1 <= 25) { // a-z? 3122 3098 // get the b-o-l 3123 q = mark[ (unsigned char)c1];3099 q = mark[c1]; 3124 3100 if (text <= q && q < end) { 3125 3101 dot = q; … … 3140 3116 // correct location! It could be off by many lines! 3141 3117 // Well..., at least its quick and dirty. 3142 c1 = get_one_char(); 3143 c1 = tolower(c1); 3144 if (islower(c1)) { 3145 c1 = c1 - 'a'; 3118 c1 = (get_one_char() | 0x20) - 'a'; 3119 if ((unsigned)c1 <= 25) { // a-z? 3146 3120 // remember the line 3147 mark[ (int)c1] = dot;3121 mark[c1] = dot; 3148 3122 } else { 3149 3123 indicate_error(c); … … 3153 3127 case 'p': // p- put register after 3154 3128 p = reg[YDreg]; 3155 if (p == 0) {3156 psbs("Nothing in register %c", what_reg());3129 if (p == NULL) { 3130 status_line_bold("Nothing in register %c", what_reg()); 3157 3131 break; 3158 3132 } … … 3174 3148 dot_right(); // move to right, can move to NL 3175 3149 } 3176 dot =string_insert(dot, p); // insert the string3150 string_insert(dot, p); // insert the string 3177 3151 end_cmd_q(); // stop adding to q 3178 3152 break; … … 3182 3156 q = end_line(dot); 3183 3157 p = text_hole_delete(p, q); // delete cur line 3184 p = string_insert(p, reg[Ureg]); // insert orig line3158 p += string_insert(p, reg[Ureg]); // insert orig line 3185 3159 dot = p; 3186 3160 dot_skip_over_ws(); … … 3189 3163 #endif /* FEATURE_VI_YANKMARK */ 3190 3164 case '$': // $- goto end of line 3191 case VI_K_END: // Cursor Key End 3192 if (cmdcnt-- > 1) { 3165 case KEYCODE_END: // Cursor Key End 3166 if (--cmdcnt > 0) { 3167 dot_next(); 3193 3168 do_cmd(c); 3194 } // repeat cnt3169 } 3195 3170 dot = end_line(dot); 3196 3171 break; … … 3216 3191 // dont separate these two commands. 'f' depends on ';' 3217 3192 // 3218 //**** fall thr uto ... ';'3193 //**** fall through to ... ';' 3219 3194 case ';': // ;- look at rest of line for last forward char 3220 if ( cmdcnt-- > 1) {3195 if (--cmdcnt > 0) { 3221 3196 do_cmd(';'); 3222 } // repeat cnt3197 } 3223 3198 if (last_forward_char == 0) 3224 3199 break; … … 3230 3205 dot = q; 3231 3206 break; 3207 case ',': // repeat latest 'f' in opposite direction 3208 if (--cmdcnt > 0) { 3209 do_cmd(','); 3210 } 3211 if (last_forward_char == 0) 3212 break; 3213 q = dot - 1; 3214 while (q >= text && *q != '\n' && *q != last_forward_char) { 3215 q--; 3216 } 3217 if (q >= text && *q == last_forward_char) 3218 dot = q; 3219 break; 3220 3232 3221 case '-': // -- goto prev line 3233 if ( cmdcnt-- > 1) {3222 if (--cmdcnt > 0) { 3234 3223 do_cmd(c); 3235 } // repeat cnt3224 } 3236 3225 dot_prev(); 3237 3226 dot_skip_over_ws(); … … 3241 3230 // Stuff the last_modifying_cmd back into stdin 3242 3231 // and let it be re-executed. 3243 if (last_modifying_cmd != 0) { 3232 if (lmc_len > 0) { 3233 last_modifying_cmd[lmc_len] = 0; 3244 3234 ioq = ioq_start = xstrdup(last_modifying_cmd); 3245 3235 } … … 3252 3242 buf[1] = '\0'; 3253 3243 q = get_input_line(buf); // get input line- use "status line" 3254 if (q[0] && !q[1]) 3244 if (q[0] && !q[1]) { 3245 if (last_search_pattern[0]) 3246 last_search_pattern[0] = c; 3255 3247 goto dc3; // if no pat re-use old pat 3248 } 3256 3249 if (q[0]) { // strlen(q) > 1: new pat- save it and find 3257 3250 // there is a new pat … … 3263 3256 break; 3264 3257 case 'N': // N- backward search for last pattern 3265 if ( cmdcnt-- > 1) {3258 if (--cmdcnt > 0) { 3266 3259 do_cmd(c); 3267 } // repeat cnt3260 } 3268 3261 dir = BACK; // assume BACKWARD search 3269 3262 p = dot - 1; … … 3277 3270 // search rest of text[] starting at next char 3278 3271 // if search fails return orignal "p" not the "p+1" address 3279 if ( cmdcnt-- > 1) {3272 if (--cmdcnt > 0) { 3280 3273 do_cmd(c); 3281 } // repeat cnt3274 } 3282 3275 dc3: 3283 if (last_search_pattern == 0) { 3284 msg = "No previous regular expression"; 3285 goto dc2; 3286 } 3287 if (last_search_pattern[0] == '/') { 3288 dir = FORWARD; // assume FORWARD search 3289 p = dot + 1; 3290 } 3276 dir = FORWARD; // assume FORWARD search 3277 p = dot + 1; 3291 3278 if (last_search_pattern[0] == '?') { 3292 3279 dir = BACK; … … 3317 3304 dc2: 3318 3305 if (*msg) 3319 psbs("%s", msg);3306 status_line_bold("%s", msg); 3320 3307 break; 3321 3308 case '{': // {- move backward paragraph … … 3358 3345 if (cnt <= 0) 3359 3346 break; 3360 if (strnc asecmp(p, "quit", cnt) == 03361 || strnc asecmp(p, "q!", cnt) == 0 // delete lines3347 if (strncmp(p, "quit", cnt) == 0 3348 || strncmp(p, "q!", cnt) == 0 // delete lines 3362 3349 ) { 3363 3350 if (file_modified && p[1] != '!') { 3364 psbs("No write since last change (:quit! overrides)");3351 status_line_bold("No write since last change (:quit! overrides)"); 3365 3352 } else { 3366 3353 editing = 0; 3367 3354 } 3368 } else if (strnc asecmp(p, "write", cnt) == 03369 || strnc asecmp(p, "wq", cnt) == 03370 || strnc asecmp(p, "wn", cnt) == 03371 || strncasecmp(p, "x", cnt) == 03355 } else if (strncmp(p, "write", cnt) == 0 3356 || strncmp(p, "wq", cnt) == 0 3357 || strncmp(p, "wn", cnt) == 0 3358 || (p[0] == 'x' && !p[1]) 3372 3359 ) { 3373 3360 cnt = file_write(current_filename, text, end - 1); 3374 3361 if (cnt < 0) { 3375 3362 if (cnt == -1) 3376 psbs("Write error: %s", strerror(errno));3363 status_line_bold("Write error: %s", strerror(errno)); 3377 3364 } else { 3378 3365 file_modified = 0; 3379 3366 last_file_modified = -1; 3380 psb("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt);3367 status_line("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt); 3381 3368 if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n' 3382 3369 || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N' … … 3385 3372 } 3386 3373 } 3387 } else if (strnc asecmp(p, "file", cnt) == 0) {3374 } else if (strncmp(p, "file", cnt) == 0) { 3388 3375 last_status_cksum = 0; // force status update 3389 3376 } else if (sscanf(p, "%d", &j) > 0) { 3390 3377 dot = find_line(j); // go to line # j 3391 3378 dot_skip_over_ws(); 3392 } else { // unrecogni sed cmd3393 n i(p);3379 } else { // unrecognized cmd 3380 not_implemented(p); 3394 3381 } 3395 3382 #endif /* !FEATURE_VI_COLON */ … … 3427 3414 case 'A': // A- append at e-o-l 3428 3415 dot_end(); // go to e-o-l 3429 //**** fall thr uto ... 'a'3416 //**** fall through to ... 'a' 3430 3417 case 'a': // a- append after current char 3431 3418 if (*dot != '\n') … … 3436 3423 case 'E': // E- end of a blank-delimited word 3437 3424 case 'W': // W- forward a blank-delimited word 3438 if ( cmdcnt-- > 1) {3425 if (--cmdcnt > 0) { 3439 3426 do_cmd(c); 3440 } // repeat cnt3427 } 3441 3428 dir = FORWARD; 3442 3429 if (c == 'B') … … 3462 3449 #endif 3463 3450 break; 3451 case 'g': // 'gg' goto a line number (vim) (default: very first line) 3452 c1 = get_one_char(); 3453 if (c1 != 'g') { 3454 buf[0] = 'g'; 3455 buf[1] = c1; // TODO: if Unicode? 3456 buf[2] = '\0'; 3457 not_implemented(buf); 3458 break; 3459 } 3460 if (cmdcnt == 0) 3461 cmdcnt = 1; 3462 /* fall through */ 3464 3463 case 'G': // G- goto to a line number (default= E-O-F) 3465 3464 dot = end - 1; // assume E-O-F … … 3474 3473 cmdcnt = (rows - 1); 3475 3474 } 3476 if ( cmdcnt-- > 1) {3475 if (--cmdcnt > 0) { 3477 3476 do_cmd('+'); 3478 } // repeat cnt3477 } 3479 3478 dot_skip_over_ws(); 3480 3479 break; … … 3482 3481 dot_begin(); // 0 3483 3482 dot_skip_over_ws(); 3484 //**** fall thr uto ... 'i'3483 //**** fall through to ... 'i' 3485 3484 case 'i': // i- insert before current char 3486 case VI_K_INSERT: // Cursor Key Insert3485 case KEYCODE_INSERT: // Cursor Key Insert 3487 3486 dc_i: 3488 3487 cmd_mode = 1; // start insrting 3489 3488 break; 3490 3489 case 'J': // J- join current and next lines together 3491 if ( cmdcnt-- > 2) {3490 if (--cmdcnt > 1) { 3492 3491 do_cmd(c); 3493 } // repeat cnt3492 } 3494 3493 dot_end(); // move to NL 3495 3494 if (dot < end - 1) { // make sure not last char in text[] … … 3507 3506 cmdcnt = (rows - 1); 3508 3507 } 3509 if ( cmdcnt-- > 1) {3508 if (--cmdcnt > 0) { 3510 3509 do_cmd('-'); 3511 } // repeat cnt3510 } 3512 3511 dot_begin(); 3513 3512 dot_skip_over_ws(); … … 3537 3536 cmd_mode = 2; 3538 3537 break; 3538 case KEYCODE_DELETE: 3539 c = 'x'; 3540 // fall through 3539 3541 case 'X': // X- delete char before dot 3540 3542 case 'x': // x- delete the current char 3541 3543 case 's': // s- substitute the current char 3542 if ( cmdcnt-- > 1) {3544 if (--cmdcnt > 0) { 3543 3545 do_cmd(c); 3544 } // repeat cnt3546 } 3545 3547 dir = 0; 3546 3548 if (c == 'X') … … 3564 3566 if (file_modified) { 3565 3567 if (ENABLE_FEATURE_VI_READONLY && readonly_mode) { 3566 psbs("\"%s\" File is read only", current_filename);3568 status_line_bold("\"%s\" File is read only", current_filename); 3567 3569 break; 3568 3570 } … … 3570 3572 if (cnt < 0) { 3571 3573 if (cnt == -1) 3572 psbs("Write error: %s", strerror(errno));3574 status_line_bold("Write error: %s", strerror(errno)); 3573 3575 } else if (cnt == (end - 1 - text + 1)) { 3574 3576 editing = 0; … … 3584 3586 case 'b': // b- back a word 3585 3587 case 'e': // e- end of word 3586 if ( cmdcnt-- > 1) {3588 if (--cmdcnt > 0) { 3587 3589 do_cmd(c); 3588 } // repeat cnt3590 } 3589 3591 dir = FORWARD; 3590 3592 if (c == 'b') … … 3608 3610 case 'Y': // Y- Yank a line 3609 3611 #endif 3612 { 3613 int yf, ml, whole = 0; 3610 3614 yf = YANKDEL; // assume either "c" or "d" 3611 3615 #if ENABLE_FEATURE_VI_YANKMARK … … 3616 3620 if (c != 'Y') 3617 3621 c1 = get_one_char(); // get the type of thing to delete 3618 find_range(&p, &q, c1); 3622 // determine range, and whether it spans lines 3623 ml = find_range(&p, &q, c1); 3619 3624 if (c1 == 27) { // ESC- user changed mind and wants out 3620 3625 c = c1 = 27; // Escape- do nothing … … 3628 3633 } 3629 3634 } 3630 dot = yank_delete(p, q, 0, yf); // delete word 3631 } else if (strchr("^0bBeEft$", c1)) { 3632 // single line copy text into a register and delete 3633 dot = yank_delete(p, q, 0, yf); // delete word 3634 } else if (strchr("cdykjHL%+-{}\r\n", c1)) { 3635 // multiple line copy text into a register and delete 3636 dot = yank_delete(p, q, 1, yf); // delete lines 3635 dot = yank_delete(p, q, ml, yf); // delete word 3636 } else if (strchr("^0bBeEft%$ lh\b\177", c1)) { 3637 // partial line copy text into a register and delete 3638 dot = yank_delete(p, q, ml, yf); // delete word 3639 } else if (strchr("cdykjHL+-{}\r\n", c1)) { 3640 // whole line copy text into a register and delete 3641 dot = yank_delete(p, q, ml, yf); // delete lines 3642 whole = 1; 3643 } else { 3644 // could not recognize object 3645 c = c1 = 27; // error- 3646 ml = 0; 3647 indicate_error(c); 3648 } 3649 if (ml && whole) { 3637 3650 if (c == 'c') { 3638 3651 dot = char_insert(dot, '\n'); 3639 3652 // on the last line of file don't move to prev line 3640 if ( dot != (end-1)) {3653 if (whole && dot != (end-1)) { 3641 3654 dot_prev(); 3642 3655 } … … 3645 3658 dot_skip_over_ws(); 3646 3659 } 3647 } else {3648 // could not recognize object3649 c = c1 = 27; // error-3650 indicate_error(c);3651 3660 } 3652 3661 if (c1 != 27) { … … 3669 3678 cnt++; 3670 3679 } 3671 psb("%s %d lines (%d chars) using [%c]",3680 status_line("%s %d lines (%d chars) using [%c]", 3672 3681 buf, cnt, strlen(reg[YDreg]), what_reg()); 3673 3682 #endif … … 3675 3684 } 3676 3685 break; 3686 } 3677 3687 case 'k': // k- goto prev line, same col 3678 case VI_K_UP: // cursor key Up3679 if ( cmdcnt-- > 1) {3688 case KEYCODE_UP: // cursor key Up 3689 if (--cmdcnt > 0) { 3680 3690 do_cmd(c); 3681 } // repeat cnt3691 } 3682 3692 dot_prev(); 3683 3693 dot = move_to_col(dot, ccol + offset); // try stay in same col … … 3687 3697 if (*dot != '\n') { 3688 3698 *dot = c1; 3689 file_modified++; // has the file been modified3699 file_modified++; 3690 3700 } 3691 3701 end_cmd_q(); // stop adding to q … … 3696 3706 if (*dot == last_forward_char) 3697 3707 dot_left(); 3698 last_forward_char = 0;3708 last_forward_char = 0; 3699 3709 break; 3700 3710 case 'w': // w- forward a word 3701 if ( cmdcnt-- > 1) {3711 if (--cmdcnt > 0) { 3702 3712 do_cmd(c); 3703 } // repeat cnt3713 } 3704 3714 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM 3705 3715 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM); … … 3727 3737 break; 3728 3738 case '~': // ~- flip the case of letters a-z -> A-Z 3729 if ( cmdcnt-- > 1) {3739 if (--cmdcnt > 0) { 3730 3740 do_cmd(c); 3731 } // repeat cnt3741 } 3732 3742 if (islower(*dot)) { 3733 3743 *dot = toupper(*dot); 3734 file_modified++; // has the file been modified3744 file_modified++; 3735 3745 } else if (isupper(*dot)) { 3736 3746 *dot = tolower(*dot); 3737 file_modified++; // has the file been modified3747 file_modified++; 3738 3748 } 3739 3749 dot_right(); … … 3741 3751 break; 3742 3752 //----- The Cursor and Function Keys ----------------------------- 3743 case VI_K_HOME: // Cursor Key Home3753 case KEYCODE_HOME: // Cursor Key Home 3744 3754 dot_begin(); 3745 3755 break; 3746 3756 // The Fn keys could point to do_macro which could translate them 3747 case VI_K_FUN1: // Function Key F1 3748 case VI_K_FUN2: // Function Key F2 3749 case VI_K_FUN3: // Function Key F3 3750 case VI_K_FUN4: // Function Key F4 3751 case VI_K_FUN5: // Function Key F5 3752 case VI_K_FUN6: // Function Key F6 3753 case VI_K_FUN7: // Function Key F7 3754 case VI_K_FUN8: // Function Key F8 3755 case VI_K_FUN9: // Function Key F9 3756 case VI_K_FUN10: // Function Key F10 3757 case VI_K_FUN11: // Function Key F11 3758 case VI_K_FUN12: // Function Key F12 3759 break; 3757 #if 0 3758 case KEYCODE_FUN1: // Function Key F1 3759 case KEYCODE_FUN2: // Function Key F2 3760 case KEYCODE_FUN3: // Function Key F3 3761 case KEYCODE_FUN4: // Function Key F4 3762 case KEYCODE_FUN5: // Function Key F5 3763 case KEYCODE_FUN6: // Function Key F6 3764 case KEYCODE_FUN7: // Function Key F7 3765 case KEYCODE_FUN8: // Function Key F8 3766 case KEYCODE_FUN9: // Function Key F9 3767 case KEYCODE_FUN10: // Function Key F10 3768 case KEYCODE_FUN11: // Function Key F11 3769 case KEYCODE_FUN12: // Function Key F12 3770 break; 3771 #endif 3760 3772 } 3761 3773 … … 3782 3794 } 3783 3795 3796 /* NB! the CRASHME code is unmaintained, and doesn't currently build */ 3784 3797 #if ENABLE_FEATURE_VI_CRASHME 3785 3798 static int totalcmds = 0; … … 3791 3804 static int Pp = 99; // Put command Probability 3792 3805 static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0; 3793 const char chars[20] = "\t012345 abcdABCD-=.$";3794 const char *const words[20] = {3806 static const char chars[20] = "\t012345 abcdABCD-=.$"; 3807 static const char *const words[20] = { 3795 3808 "this", "is", "a", "test", 3796 3809 "broadcast", "the", "emergency", "of", … … 3799 3812 "back", "January", "Febuary", "March" 3800 3813 }; 3801 const char *const lines[20] = {3814 static const char *const lines[20] = { 3802 3815 "You should have received a copy of the GNU General Public License\n", 3803 3816 "char c, cm, *cmd, *cmd1;\n", … … 3821 3834 "This is too much english for a computer geek.\n", 3822 3835 }; 3823 char *multilines[20] = {3836 static char *multilines[20] = { 3824 3837 "You should have received a copy of the GNU General Public License\n", 3825 3838 "char c, cm, *cmd, *cmd1;\n", … … 3855 3868 3856 3869 // is there already a command running? 3857 if (read ed_for_parse> 0)3870 if (readbuffer[0] > 0) 3858 3871 goto cd1; 3859 3872 cd0: 3860 startrbi = rbi = 0; 3873 readbuffer[0] = 'X'; 3874 startrbi = rbi = 1; 3861 3875 sleeptime = 0; // how long to pause between commands 3862 memset(readbuffer, '\0', MAX_LINELEN); // clear the read buffer3876 memset(readbuffer, '\0', sizeof(readbuffer)); 3863 3877 // generate a command by percentages 3864 3878 percent = (int) lrand48() % 100; // get a number from 0-99 … … 3932 3946 strcat(readbuffer, "\033"); 3933 3947 } 3934 read ed_for_parse = strlen(readbuffer);3948 readbuffer[0] = strlen(readbuffer + 1); 3935 3949 cd1: 3936 3950 totalcmds++; … … 3945 3959 3946 3960 time_t tim; 3947 char d[2], msg[ MAX_LINELEN];3961 char d[2], msg[80]; 3948 3962 3949 3963 msg[0] = '\0'; … … 3968 3982 3969 3983 if (msg[0]) { 3970 alarm(0);3971 3984 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s", 3972 3985 totalcmds, last_input_char, msg, SOs, SOn); 3973 fflush (stdout);3974 while ( read(0, d, 1) > 0) {3986 fflush_all(); 3987 while (safe_read(STDIN_FILENO, d, 1) > 0) { 3975 3988 if (d[0] == '\n' || d[0] == '\r') 3976 3989 break; 3977 3990 } 3978 alarm(3); 3979 } 3980 tim = (time_t) time((time_t *) 0); 3991 } 3992 tim = time(NULL); 3981 3993 if (tim >= (oldtim + 3)) { 3982 3994 sprintf(status_buffer,
Note:
See TracChangeset
for help on using the changeset viewer.