Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/editors


Ignore:
Timestamp:
Feb 25, 2011, 9:26:54 PM (13 years ago)
Author:
Bruno Cornec
Message:
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
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
    12#
    23# For a description of the syntax of this configuration file,
     
    67menu "Editors"
    78
     9config PATCH
     10    bool "patch"
     11    default y
     12    help
     13      Apply a unified diff formatted patch.
     14
    815config AWK
    916    bool "awk"
    10     default n
    11     help
    12       Awk is used as a pattern scanning and processing language.  This is
     17    default y
     18    help
     19      Awk is used as a pattern scanning and processing language. This is
    1320      the BusyBox implementation of that programming language.
    1421
    15 config FEATURE_AWK_MATH
     22config FEATURE_AWK_LIBM
    1623    bool "Enable math functions (requires libm)"
    1724    default y
     
    2330config CMP
    2431    bool "cmp"
    25     default n
     32    default y
    2633    help
    2734      cmp is used to compare two files and returns the result
     
    3037config DIFF
    3138    bool "diff"
    32     default n
     39    default y
    3340    help
    3441      diff compares two files or directories and outputs the
     
    3643      the patch command.
    3744
    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.
     45config 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.
    4551
    4652config FEATURE_DIFF_DIR
     
    5258      comparison.
    5359
    54 config FEATURE_DIFF_MINIMAL
    55     bool "Enable -d option to find smaller sets of changes"
    56     default n
    57     depends on DIFF
    58     help
    59       Enabling this option allows the use of -d to make diff
    60       try hard to find the smallest possible set of changes.
    61 
    6260config ED
    6361    bool "ed"
    64     default n
     62    default y
    6563    help
    6664      The original 1970's Unix text editor, from the days of teletypes.
    67       Small, simple, evil.  Part of SUSv3. If you're not already using
     65      Small, simple, evil. Part of SUSv3. If you're not already using
    6866      this, you don't need it.
    69 
    70 config PATCH
    71     bool "patch"
    72     default n
    73     help
    74       Apply a unified diff formatted patch.
    7567
    7668config SED
    7769    bool "sed"
    78     default n
     70    default y
    7971    help
    8072      sed is used to perform text transformations on a file
     
    8375config VI
    8476    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
     84config 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
     93config FEATURE_VI_8BIT
     94    bool "Allow vi to display 8-bit chars (otherwise shows dots)"
    8595    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.
    101102
    102103config FEATURE_VI_COLON
     
    105106    depends on VI
    106107    help
    107       Enable a limited set of colon commands for vi.  This does not
     108      Enable a limited set of colon commands for vi. This does not
    108109      provide an "ex" mode.
    109110
     
    129130    depends on VI
    130131    help
    131       Selecting this option will make busybox vi signal aware.  This will
     132      Selecting this option will make busybox vi signal aware. This will
    132133      make busybox vi support SIGWINCH to deal with Window Changes, catch
    133134      Ctrl-Z and Ctrl-C and alarms.
     
    169170      Make busybox vi behave nicely with terminals that get resized.
    170171
     172config 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
    171184config FEATURE_VI_OPTIMIZE_CURSOR
    172185    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
    12# Makefile for busybox
    23#
    34# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
    45#
    5 # Licensed under the GPL v2, see the file LICENSE in this tarball.
     6# Licensed under GPLv2, see file LICENSE in this source tree.
    67
    78lib-y:=
     9
     10lib-$(CONFIG_PATCH) += patch.o
    811lib-$(CONFIG_AWK)       += awk.o
    912lib-$(CONFIG_CMP)       += cmp.o
    1013lib-$(CONFIG_DIFF)      += diff.o
    1114lib-$(CONFIG_ED)        += ed.o
    12 lib-$(CONFIG_PATCH)     += patch.o
    1315lib-$(CONFIG_SED)       += sed.o
    1416lib-$(CONFIG_VI)        += vi.o
  • branches/2.2.9/mindi-busybox/editors/awk.c

    r1765 r2725  
    55 * Copyright (C) 2002 by Dmitry Zakharov <dmit@crp.bank.gov.ua>
    66 *
    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.
    88 */
    99
     
    1111#include "xregex.h"
    1212#include <math.h>
    13 extern char **environ;
    1413
    1514/* 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
    1629
    1730
     
    3346/* these flags are static, don't change them when value is changed */
    3447#define VF_DONTTOUCH    (VF_ARRAY | VF_SPECIAL | VF_WALK | VF_CHILD | VF_DIRTY)
     48
     49typedef struct walker_list {
     50    char *end;
     51    char *cur;
     52    struct walker_list *prev;
     53    char wbuf[1];
     54} walker_list;
    3555
    3656/* Variable */
     
    4363        struct xhash_s *array;  /* array ptr */
    4464        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) */
    4666    } x;
    4767} var;
     
    95115        struct node_s *n;
    96116        var *v;
    97         int i;
    98         char *s;
     117        int aidx;
     118        char *new_progname;
    99119        regex_t *re;
    100120    } l;
     
    103123        regex_t *ire;
    104124        func *f;
    105         int argno;
    106125    } r;
    107126    union {
     
    116135    struct nvblock_s *prev;
    117136    struct nvblock_s *next;
    118     var nv[0];
     137    var nv[];
    119138} nvblock;
    120139
     
    252271/* builtins */
    253272enum {
    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,
    255274    B_ge,   B_gs,   B_su,
    256275    B_an,   B_co,   B_ls,   B_or,   B_rs,   B_xo,
     
    259278/* tokens and their corresponding info values */
    260279
    261 #define NTC     "\377"  /* switch to next token class (tc<<1) */
    262 #define NTCC    '\377'
    263 
    264 #define OC_B    OC_BUILTIN
     280#define NTC     "\377"  /* switch to next token class (tc<<1) */
     281#define NTCC    '\377'
     282
     283#define OC_B  OC_BUILTIN
    265284
    266285static 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" */
    309329    ;
    310330
     
    313333    0,
    314334    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 */
    334346    OC_COMMA|SS|P(80),
    335347    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, /* ] */
    338350    0,
    339351    0,
    340352    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,
    347358    ST_WHILE,
    348     0,
     359    0, /* else */
    349360
    350361    OC_B|B_an|P(0x83), OC_B|B_co|P(0x41), OC_B|B_ls|P(0x83), OC_B|B_or|P(0x83),
     
    355366    OC_B|B_ge|P(0xd6), OC_B|B_gs|P(0xb6), OC_B|B_ix|P(0x9b), OC_FBLTIN|Sx|F_le,
    356367    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),
    358369    OC_B|B_lo|P(0x49), OC_B|B_up|P(0x49),
    359370    OC_GETLINE|SV|P(0),
    360     0,  0,
     371    0,                 0,
    361372    0,
    362     0
     373    0 /* END */
    363374};
    364375
     
    368379    CONVFMT,    OFMT,       FS,         OFS,
    369380    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
    374384};
    375385
     
    377387    "CONVFMT\0" "OFMT\0"    "FS\0*"     "OFS\0"
    378388    "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";
    383392
    384393static const char vValues[] ALIGN1 =
    385394    "%.6g\0"    "%.6g\0"    " \0"       " \0"
    386395    "\n\0"      "\n\0"      "\0"        "\0"
    387     "\034\0"
    388     "\377";
     396    "\034\0"    "\0"        "\377";
    389397
    390398/* hash size may grow to these values */
     
    394402
    395403/* 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 */
    397408struct globals {
    398     chain beginseq, mainseq, endseq, *seq;
     409    double t_double;
     410    chain beginseq, mainseq, endseq;
     411    chain *seq;
    399412    node *break_ptr, *continue_ptr;
    400413    rstream *iF;
     
    443456
    444457    /* biggest and least used members go last */
    445     double t_double;
    446458    tsplitter fsplitter, rsplitter;
    447459};
    448460#define G1 (ptr_to_globals[-1])
    449 #define G (*(struct globals2 *const)ptr_to_globals)
     461#define G (*(struct globals2 *)ptr_to_globals)
    450462/* 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 */
    453465/* 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    )
    455468#define beginseq     (G1.beginseq    )
    456469#define mainseq      (G1.mainseq     )
     
    480493#define t_tclass     (G.t_tclass    )
    481494#define t_string     (G.t_string    )
    482 #define t_double     (G.t_double    )
    483495#define t_lineno     (G.t_lineno    )
    484496#define t_rollback   (G.t_rollback  )
     
    487499#define rsplitter    (G.rsplitter   )
    488500#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)); \
    490502    G.next_token__ltclass = TC_OPTERM; \
    491503    G.evaluate__seed = 1; \
     
    500512static rstream *next_input_file(void);
    501513static int fmt_num(char *, int, const char *, double, int);
    502 static int awk_exit(int) ATTRIBUTE_NORETURN;
     514static int awk_exit(int) NORETURN;
    503515
    504516/* ---- error handling ---- */
     
    513525static const char EMSG_POSSIBLE_ERROR[] ALIGN1 = "Possible syntax error";
    514526static const char EMSG_UNDEF_FUNC[] ALIGN1 = "Call to undefined function";
    515 #if !ENABLE_FEATURE_AWK_MATH
    516527static const char EMSG_NO_MATH[] ALIGN1 = "Math support is not compiled in";
    517 #endif
    518 
    519 static void zero_out_var(var * vp)
     528
     529static void zero_out_var(var *vp)
    520530{
    521531    memset(vp, 0, sizeof(*vp));
    522532}
    523533
    524 static void syntax_error(const char *const message) ATTRIBUTE_NORETURN;
    525 static void syntax_error(const char *const message)
     534static void syntax_error(const char *message) NORETURN;
     535static void syntax_error(const char *message)
    526536{
    527537    bb_error_msg_and_die("%s:%i: %s", g_progname, g_lineno, message);
     
    534544    unsigned idx = 0;
    535545
    536     while (*name) idx = *name++ + (idx << 6) - idx;
     546    while (*name)
     547        idx = *name++ + (idx << 6) - idx;
    537548    return idx;
    538549}
     
    543554    xhash *newhash;
    544555
    545     newhash = xzalloc(sizeof(xhash));
     556    newhash = xzalloc(sizeof(*newhash));
    546557    newhash->csize = FIRST_PRIME;
    547     newhash->items = xzalloc(newhash->csize * sizeof(hash_item *));
     558    newhash->items = xzalloc(FIRST_PRIME * sizeof(newhash->items[0]));
    548559
    549560    return newhash;
     
    555566    hash_item *hi;
    556567
    557     hi = hash->items [ hashidx(name) % hash->csize ];
     568    hi = hash->items[hashidx(name) % hash->csize];
    558569    while (hi) {
    559570        if (strcmp(hi->name, name) == 0)
    560             return &(hi->data);
     571            return &hi->data;
    561572        hi = hi->next;
    562573    }
     
    574585
    575586    newsize = PRIMES[hash->nprime++];
    576     newitems = xzalloc(newsize * sizeof(hash_item *));
     587    newitems = xzalloc(newsize * sizeof(newitems[0]));
    577588
    578589    for (i = 0; i < hash->csize; i++) {
     
    605616
    606617        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);
    609620
    610621        idx = hashidx(name) % hash->csize;
     
    613624        hash->glen += l;
    614625    }
    615     return &(hi->data);
     626    return &hi->data;
    616627}
    617628
     
    625636    hash_item *hi, **phi;
    626637
    627     phi = &(hash->items[hashidx(name) % hash->csize]);
     638    phi = &hash->items[hashidx(name) % hash->csize];
    628639    while (*phi) {
    629640        hi = *phi;
     
    635646            break;
    636647        }
    637         phi = &(hi->next);
     648        phi = &hi->next;
    638649    }
    639650}
     
    641652/* ------ some useful functions ------ */
    642653
    643 static void skip_spaces(char **s)
    644 {
    645     char *p = *s;
    646 
     654static char *skip_spaces(char *p)
     655{
    647656    while (1) {
    648657        if (*p == '\\' && p[1] == '\n') {
     
    654663        p++;
    655664    }
    656     *s = p;
    657 }
    658 
     665    return p;
     666}
     667
     668/* returns old *s, advances *s past word and terminating NUL */
    659669static char *nextword(char **s)
    660670{
    661671    char *p = *s;
    662 
    663     while (*(*s)++) /* */;
    664 
     672    while (*(*s)++ != '\0')
     673        continue;
    665674    return p;
    666675}
     
    670679    char c, *pps;
    671680
    672     c = *((*s)++);
     681    c = *(*s)++;
    673682    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    }
    676690    return c;
    677691}
    678692
    679 static int ALWAYS_INLINE isalnum_(int c)
     693static ALWAYS_INLINE int isalnum_(int c)
    680694{
    681695    return (isalnum(c) || c == '_');
    682696}
    683697
    684 static FILE *afopen(const char *path, const char *mode)
    685 {
    686     return (*path == '-' && *(path+1) == '\0') ? stdin : xfopen(path, mode);
     698static 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);
    687719}
    688720
     
    748780}
    749781
    750 /* same as setvar_s but set USER flag */
     782/* same as setvar_s but sets USER flag */
    751783static var *setvar_u(var *v, const char *value)
    752784{
    753     setvar_s(v, value);
     785    v = setvar_s(v, value);
    754786    v->type |= VF_USER;
    755787    return v;
     
    759791static void setari_u(var *a, int idx, const char *s)
    760792{
    761     char sidx[sizeof(int)*3 + 1];
    762793    var *v;
    763794
    764     sprintf(sidx, "%d", idx);
    765     v = findvar(iamarray(a), sidx);
     795    v = findvar(iamarray(a), itoa(idx));
    766796    setvar_u(v, s);
    767797}
     
    796826        s = v->string;
    797827        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);
    799831            if (v->type & VF_USER) {
    800                 skip_spaces(&s);
     832                s = skip_spaces(s);
    801833                if (*s != '\0')
    802834                    v->type &= ~VF_USER;
    803835            }
    804836        } else {
     837            debug_printf_eval("getvar_i: '%s'->zero\n", s);
    805838            v->type &= ~VF_USER;
    806839        }
    807840        v->type |= VF_CACHED;
    808841    }
     842    debug_printf_eval("getvar_i: %f\n", v->number);
    809843    return v->number;
     844}
     845
     846/* Used for operands of bitwise ops */
     847static 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);
    810857}
    811858
     
    815862        clrvar(dest);
    816863        dest->type |= (src->type & ~(VF_DONTTOUCH | VF_FSTR));
     864        debug_printf_eval("copyvar: number:%f string:'%s'\n", src->number, src->string);
    817865        dest->number = src->number;
    818866        if (src->string)
     
    825873static var *incvar(var *v)
    826874{
    827     return setvar_i(v, getvar_i(v) + 1.);
     875    return setvar_i(v, getvar_i(v) + 1.0);
    828876}
    829877
     
    839887{
    840888    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]);
    843891}
    844892
     
    852900    while (g_cb) {
    853901        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;
    855904        g_cb = g_cb->next;
    856905    }
     
    858907    if (!g_cb) {
    859908        size = (n <= MINNVBLOCK) ? MINNVBLOCK : n;
    860         g_cb = xmalloc(sizeof(nvblock) + size * sizeof(var));
     909        g_cb = xzalloc(sizeof(nvblock) + size * sizeof(var));
    861910        g_cb->size = size;
    862911        g_cb->pos = g_cb->nv;
    863912        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;
    866916    }
    867917
     
    891941            free(p->x.array);
    892942        }
    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        }
    896955        clrvar(p);
    897956    }
     
    916975#define ltclass         (G.next_token__ltclass)
    917976
    918     char *p, *pp, *s;
     977    char *p, *s;
    919978    const char *tl;
    920979    uint32_t tc;
    921980    const uint32_t *ti;
    922     int l;
    923981
    924982    if (t_rollback) {
     
    933991        p = g_pos;
    934992 readnext:
    935         skip_spaces(&p);
     993        p = skip_spaces(p);
    936994        g_lineno = t_lineno;
    937995        if (*p == '#')
     
    9491007            t_string = s = ++p;
    9501008            while (*p != '\"') {
     1009                char *pp;
    9511010                if (*p == '\0' || *p == '\n')
    9521011                    syntax_error(EMSG_UNEXP_EOS);
    953                 *(s++) = nextchar(&p);
     1012                pp = p;
     1013                *s++ = nextchar(&pp);
     1014                p = pp;
    9541015            }
    9551016            p++;
     
    9651026                *s = *p++;
    9661027                if (*s++ == '\\') {
    967                     pp = p;
    968                     *(s-1) = bb_process_escape_sequence((const char **)&p);
    969                     if (*pp == '\\')
     1028                    char *pp = p;
     1029                    s[-1] = bb_process_escape_sequence((const char **)&pp);
     1030                    if (*p == '\\')
    9701031                        *s++ = '\\';
    971                     if (p == pp)
     1032                    if (pp == p)
    9721033                        *s++ = *p++;
     1034                    else
     1035                        p = pp;
    9731036                }
    9741037            }
     
    9791042        } else if (*p == '.' || isdigit(*p)) {
    9801043            /* it's a number */
    981             t_double = strtod(p, &p);
     1044            char *pp = p;
     1045            t_double = my_strtod(&pp);
     1046            p = pp;
    9821047            if (*p == '.')
    9831048                syntax_error(EMSG_UNEXP_TOKEN);
     
    9901055            ti = tokeninfo;
    9911056            while (*tl) {
    992                 l = *(tl++);
    993                 if (l == NTCC) {
     1057                int l = (unsigned char) *tl++;
     1058                if (l == (unsigned char) NTCC) {
    9941059                    tc <<= 1;
    9951060                    continue;
    9961061                }
    997                 /* if token class is expected, token
    998                  * matches and it's not a longer word,
    999                  * then this is what we are looking for
     1062                /* if token class is expected,
     1063                 * token matches,
     1064                 * and it's not a longer word,
    10001065                 */
    10011066                if ((tc & (expected | TC_WORD | TC_NEWLINE))
    1002                  && *tl == *p && strncmp(p, tl, l) == 0
     1067                 && strncmp(p, tl, l) == 0
    10031068                 && !((tc & TC_WORD) && isalnum_(p[l]))
    10041069                ) {
     1070                    /* then this is what we are looking for */
    10051071                    t_info = *ti;
    10061072                    p += l;
    1007                     break;
     1073                    goto token_found;
    10081074                }
    10091075                ti++;
    10101076                tl += l;
    10111077            }
    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;
    10231099                }
    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: ;
    10381102        }
    10391103        g_pos = p;
     
    10831147}
    10841148
    1085 static node *mk_re_node(const char *s, node *n, regex_t *re)
     1149static void mk_re_node(const char *s, node *n, regex_t *re)
    10861150{
    10871151    n->info = OC_REGEXP;
     
    10901154    xregcomp(re, s, REG_EXTENDED);
    10911155    xregcomp(re + 1, s, REG_EXTENDED | REG_ICASE);
    1092 
    1093     return n;
    10941156}
    10951157
     
    11151177
    11161178    while (!((tc = next_token(xtc)) & iexp)) {
     1179
    11171180        if (glptr && (t_info == (OC_COMPARE | VV | P(39) | 2))) {
    11181181            /* input redirection (<) attached to glptr node */
     
    11261189             * previous operators with higher priority */
    11271190            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            ) {
    11301194                vn = vn->a.n;
     1195            }
    11311196            if ((t_info & OPCLSMASK) == OC_TERNARY)
    11321197                t_info += P(6);
     
    11671232                    if (v != NULL) {
    11681233                        cn->info = OC_FNARG;
    1169                         cn->l.i = v->x.aidx;
     1234                        cn->l.aidx = v->x.aidx;
    11701235                    } else {
    11711236                        cn->l.v = newvar(t_string);
     
    12281293        seq->programname = g_progname;
    12291294        n = chain_node(OC_NEWSOURCE);
    1230         n->l.s = xstrdup(g_progname);
     1295        n->l.new_progname = xstrdup(g_progname);
    12311296    }
    12321297
     
    12831348    if (c & TC_GRPSTART) {
    12841349        while (next_token(TC_GRPSEQ | TC_GRPTERM) != TC_GRPTERM) {
    1285             if (t_tclass & TC_NEWLINE) continue;
     1350            if (t_tclass & TC_NEWLINE)
     1351                continue;
    12861352            rollback_token();
    12871353            chain_group();
     
    14051471            while (next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) {
    14061472                v = findvar(ahash, t_string);
    1407                 v->x.aidx = (f->nargs)++;
     1473                v->x.aidx = f->nargs++;
    14081474
    14091475                if (next_token(TC_COMMA | TC_SEQTERM) & TC_SEQTERM)
    14101476                    break;
    14111477            }
    1412             seq = &(f->body);
     1478            seq = &f->body;
    14131479            chain_group();
    14141480            clear_array(ahash);
     
    14481514        regfree(ire); // TODO: nuke ire, use re+1?
    14491515    }
    1450     if (strlen(s) > 1) {
     1516    if (s[0] && s[1]) { /* strlen(s) > 1 */
    14511517        mk_re_node(s, n, re);
    14521518    } else {
    1453         n->info = (uint32_t) *s;
     1519        n->info = (uint32_t) s[0];
    14541520    }
    14551521
     
    14631529static regex_t *as_regex(node *op, regex_t *preg)
    14641530{
     1531    int cflags;
    14651532    var *v;
    14661533    const char *s;
     
    14711538    v = nvalloc(1);
    14721539    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    }
    14741551    nvfree(v);
    14751552    return preg;
    14761553}
    14771554
    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 */
     1559static 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;
    14831566}
    14841567
     
    14911574        i = maxfields;
    14921575        maxfields = size + 16;
    1493         Fields = xrealloc(Fields, maxfields * sizeof(var));
     1576        Fields = xrealloc(Fields, maxfields * sizeof(Fields[0]));
    14941577        for (; i < maxfields; i++) {
    14951578            Fields[i].type = VF_SPECIAL;
     
    14971580        }
    14981581    }
    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);
    15041585    }
    15051586    nfields = size;
     
    15081589static int awk_split(const char *s, node *spl, char **slist)
    15091590{
    1510     int l, n = 0;
     1591    int l, n;
    15111592    char c[4];
    15121593    char *s1;
     
    15221603        c[2] = '\n';
    15231604
     1605    n = 0;
    15241606    if ((spl->info & OPCLSMASK) == OC_REGEXP) {  /* regex split */
    15251607        if (!*s)
     
    15391621            } else {
    15401622                pmatch[0].rm_eo = l;
    1541                 if (s[l]) pmatch[0].rm_eo++;
     1623                if (s[l])
     1624                    pmatch[0].rm_eo++;
    15421625            }
    15431626            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);
    15451631            nextword(&s1);
    15461632            s += pmatch[0].rm_eo;
     
    15611647            c[1] = tolower(c[1]);
    15621648        }
    1563         if (*s1) n++;
    1564         while ((s1 = strpbrk(s1, c))) {
     1649        if (*s1)
     1650            n++;
     1651        while ((s1 = strpbrk(s1, c)) != NULL) {
    15651652            *s1++ = '\0';
    15661653            n++;
     
    15711658    while (*s) {
    15721659        s = skip_whitespace(s);
    1573         if (!*s) break;
     1660        if (!*s)
     1661            break;
    15741662        n++;
    15751663        while (*s && !isspace(*s))
     
    16361724                len += sl;
    16371725            }
    1638             qrealloc(&b, len+l+sl, &bsize);
     1726            b = qrealloc(b, len+l+sl, &bsize);
    16391727            memcpy(b+len, s, l);
    16401728            len += l;
     
    16811769static void hashwalk_init(var *v, xhash *array)
    16821770{
    1683     char **w;
    16841771    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;
    16931788    for (i = 0; i < array->csize; i++) {
    16941789        hi = array->items[i];
    16951790        while (hi) {
    1696             strcpy(*w, hi->name);
    1697             nextword(w);
     1791            strcpy(w->end, hi->name);
     1792            nextword(&w->end);
    16981793            hi = hi->next;
    16991794        }
     
    17031798static int hashwalk_next(var *v)
    17041799{
    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;
    17091808        return FALSE;
    1710 
    1711     setvar_s(v, nextword(w+1));
     1809    }
     1810
     1811    setvar_s(v, nextword(&w->cur));
    17121812    return TRUE;
    17131813}
     
    17251825    char *b;
    17261826    regmatch_t pmatch[2];
    1727     int a, p, pp=0, size;
     1827    int size, a, p, pp = 0;
    17281828    int fd, so, eo, r, rp;
    17291829    char c, *m, *s;
     1830
     1831    debug_printf_eval("entered %s()\n", __func__);
    17301832
    17311833    /* we're using our own buffer since we need access to accumulating
     
    17401842    rp = 0;
    17411843
    1742     if (!m) qrealloc(&m, 256, &size);
     1844    if (!m)
     1845        m = qrealloc(m, 256, &size);
     1846
    17431847    do {
    17441848        b = m + a;
     
    17561860            } else if (c != '\0') {
    17571861                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);
    17591864                if (s) {
    17601865                    so = eo = s-b;
     
    17681873                if (s) {
    17691874                    so = eo = s-b;
    1770                     while (b[eo] == '\n') eo++;
     1875                    while (b[eo] == '\n')
     1876                        eo++;
    17711877                    if (b[eo] != '\0')
    17721878                        break;
     
    17761882
    17771883        if (a > 0) {
    1778             memmove(m, (const void *)(m+a), p+1);
     1884            memmove(m, m+a, p+1);
    17791885            b = m;
    17801886            a = 0;
    17811887        }
    17821888
    1783         qrealloc(&m, a+p+128, &size);
     1889        m = qrealloc(m, a+p+128, &size);
    17841890        b = m + a;
    17851891        pp = p;
     
    18111917    rsm->size = size;
    18121918
     1919    debug_printf_eval("returning from %s(): %d\n", __func__, r);
     1920
    18131921    return r;
    18141922}
     
    18351943}
    18361944
    1837 
    18381945/* formatted output into an allocated buffer, return ptr to buffer */
    18391946static char *awk_printf(node *n)
     
    18521959    while (*f) {
    18531960        s = f;
    1854         while (*f && (*f != '%' || *(++f) == '%'))
     1961        while (*f && (*f != '%' || *++f == '%'))
    18551962            f++;
    18561963        while (*f && !isalpha(*f)) {
     
    18611968
    18621969        incr = (f - s) + MAXVARFMT;
    1863         qrealloc(&b, incr + i, &bsize);
     1970        b = qrealloc(b, incr + i, &bsize);
    18641971        c = *f;
    1865         if (c != '\0') f++;
     1972        if (c != '\0')
     1973            f++;
    18661974        c1 = *f;
    18671975        *f = '\0';
     
    18741982        } else if (c == 's') {
    18751983            s1 = getvar_s(arg);
    1876             qrealloc(&b, incr+i+strlen(s1), &bsize);
     1984            b = qrealloc(b, incr+i+strlen(s1), &bsize);
    18771985            i += sprintf(b+i, s, s1);
    18781986        } else {
     
    18821990
    18831991        /* 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
    18881996    free(fmt);
    18891997    nvfree(v);
     1998    b = xrealloc(b, i + 1);
    18901999    b[i] = '\0';
    18912000    return b;
    18922001}
    18932002
    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).
    18992009 */
    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;
     2010static int awk_sub(node *rn, const char *repl, int nm, var *src, var *dest, int subexp)
     2011{
     2012    char *resbuf;
    19042013    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;
    19062016    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
    19242038            /* replace */
    1925             di -= (eo - so);
     2039            residx -= (eo - so);
    19262040            nbs = 0;
    19272041            for (s = repl; *s; s++) {
    1928                 ds[di++] = c = *s;
     2042                char c = resbuf[residx++] = *s;
    19292043                if (c == '\\') {
    19302044                    nbs++;
    19312045                    continue;
    19322046                }
    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);
    19352050                    j = 0;
    19362051                    if (c != '&') {
     
    19392054                    }
    19402055                    if (nbs % 2) {
    1941                         ds[di++] = c;
     2056                        resbuf[residx++] = c;
    19422057                    } 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;
    19472062                    }
    19482063                }
     
    19512066        }
    19522067
     2068        regexec_flags = REG_NOTBOL;
    19532069        sp += eo;
    1954         if (i == nm) break;
     2070        if (match_no == nm)
     2071            break;
    19552072        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
     2101static 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
     2129static NOINLINE var *exec_builtin(node *op, var *res)
    19692130{
    19702131#define tspl (G.exec_builtin__tspl)
    19712132
    1972     int (*to_xxx)(int);
    19732133    var *tv;
    19742134    node *an[4];
     
    19812141    int nargs;
    19822142    time_t tt;
    1983     char *s, *s1;
    19842143    int i, l, ll, n;
    19852144
     
    19912150    for (i = 0; i < 4 && op; i++) {
    19922151        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]);
    19952156        isr >>= 1;
    19962157    }
    19972158
    19982159    nargs = i;
    1999     if (nargs < (info >> 30))
     2160    if ((uint32_t)nargs < (info >> 30))
    20002161        syntax_error(EMSG_TOO_FEW_ARGS);
    20012162
    2002     switch (info & OPNMASK) {
     2163    info &= OPNMASK;
     2164    switch (info) {
    20032165
    20042166    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);
    20102171        break;
    20112172
    2012     case B_sp:
     2173    case B_sp: {
     2174        char *s, *s1;
     2175
    20132176        if (nargs > 2) {
    20142177            spl = (an[2]->info & OPCLSMASK) == OC_REGEXP ?
     
    20212184        s1 = s;
    20222185        clear_array(iamarray(av[1]));
    2023         for (i=1; i<=n; i++)
    2024             setari_u(av[1], i, nextword(&s1));
    2025         free(s);
     2186        for (i = 1; i <= n; i++)
     2187            setari_u(av[1], i, nextword(&s));
     2188        free(s1);
    20262189        setvar_i(res, n);
    20272190        break;
    2028 
    2029     case B_ss:
     2191    }
     2192
     2193    case B_ss: {
     2194        char *s;
     2195
    20302196        l = strlen(as[0]);
    20312197        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;
    20342202        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);
    20392206        setvar_p(res, s);
    20402207        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" */
    20422212    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]));
    20442214        break;
    20452215
    20462216    case B_co:
    2047         setvar_i(res, ~(long)getvar_i(av[0]));
     2217        setvar_i(res, ~getvar_i_int(av[0]));
    20482218        break;
    20492219
    20502220    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]));
    20522222        break;
    20532223
    20542224    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]));
    20562226        break;
    20572227
    20582228    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]));
    20602230        break;
    20612231
    20622232    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]));
    20642234        break;
    20652235
    20662236    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;
    20732239        s1 = s = xstrdup(as[0]);
    20742240        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);
    20762244            s1++;
    20772245        }
    20782246        setvar_p(res, s);
    20792247        break;
     2248    }
    20802249
    20812250    case B_ix:
     
    20852254        if (ll > 0 && l >= 0) {
    20862255            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;
    20892259            } else {
    20902260                /* this piece of code is terribly slow and
    20912261                 * really should be rewritten
    20922262                 */
    2093                 for (i=0; i<=l; i++) {
     2263                for (i = 0; i <= l; i++) {
    20942264                    if (strncasecmp(as[0]+i, as[1], ll) == 0) {
    20952265                        n = i+1;
     
    21152285        break;
    21162286
     2287    case B_mt:
     2288        setvar_i(res, do_mktime(as[0]));
     2289        break;
     2290
    21172291    case B_ma:
    21182292        re = as_regex(an[1], &sreg);
     
    21282302        setvar_i(newvar("RLENGTH"), pmatch[0].rm_eo - pmatch[0].rm_so);
    21292303        setvar_i(res, pmatch[0].rm_so);
    2130         if (re == &sreg) regfree(re);
     2304        if (re == &sreg)
     2305            regfree(re);
    21312306        break;
    21322307
     
    21612336/* seed is initialized to 1 */
    21622337#define seed   (G.evaluate__seed)
    2163 #define sreg   (G.evaluate__sreg)
    2164 
    2165     node *op1;
     2338#define sreg   (G.evaluate__sreg)
     2339
    21662340    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;
    21832341
    21842342    if (!op)
    21852343        return setvar_s(res, NULL);
    21862344
     2345    debug_printf_eval("entered %s()\n", __func__);
     2346
    21872347    v1 = nvalloc(2);
    21882348
    21892349    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
    21902363        opinfo = op->info;
    21912364        opn = (opinfo & OPNMASK);
    21922365        g_lineno = op->lineno;
     2366        op1 = op->l.n;
     2367        debug_printf_eval("opinfo:%08x opn:%08x\n", opinfo, opn);
    21932368
    21942369        /* 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));
    22022388        switch (XC(opinfo & OPCLSMASK)) {
    22032389
     
    22122398                    if (ptest(op1->r.n))
    22132399                        op->info &= ~OF_CHECKED;
    2214 
    22152400                    op = op->a.n;
    22162401                } else {
     
    22182403                }
    22192404            } else {
    2220                 op = (ptest(op1)) ? op->a.n : op->r.n;
     2405                op = ptest(op1) ? op->a.n : op->r.n;
    22212406            }
    22222407            break;
     
    22422427
    22432428        case XC( OC_PRINT ):
    2244         case XC( OC_PRINTF ):
    2245             X.F = stdout;
     2429        case XC( OC_PRINTF ): {
     2430            FILE *F = stdout;
     2431
    22462432            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) {
    22492435                    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)
    22522438                            bb_perror_msg_and_die("popen");
    2253                         X.rsm->is_pipe = 1;
     2439                        rsm->is_pipe = 1;
    22542440                    } else {
    2255                         X.rsm->F = xfopen(R.s, opn=='w' ? "w" : "a");
     2441                        rsm->F = xfopen(R.s, opn=='w' ? "w" : "a");
    22562442                    }
    22572443                }
    2258                 X.F = X.rsm->F;
     2444                F = rsm->F;
    22592445            }
    22602446
    22612447            if ((opinfo & OPCLSMASK) == OC_PRINT) {
    22622448                if (!op1) {
    2263                     fputs(getvar_s(intvar[F0]), X.F);
     2449                    fputs(getvar_s(intvar[F0]), F);
    22642450                } else {
    22652451                    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) {
    22682454                            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);
    22712457                        } else {
    2272                             fputs(getvar_s(L.v), X.F);
     2458                            fputs(getvar_s(v), F);
    22732459                        }
    22742460
    2275                         if (op1) fputs(getvar_s(intvar[OFS]), X.F);
     2461                        if (op1)
     2462                            fputs(getvar_s(intvar[OFS]), F);
    22762463                    }
    22772464                }
    2278                 fputs(getvar_s(intvar[ORS]), X.F);
     2465                fputs(getvar_s(intvar[ORS]), F);
    22792466
    22802467            } 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];
    22942484            } else {
    22952485                syntax_error(EMSG_NOT_ARRAY);
     
    22972487
    22982488            if (op1->r.n) {
     2489                const char *s;
    22992490                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);
    23022493            } else {
    2303                 clear_array(iamarray(R.v));
    2304             }
    2305             break;
     2494                clear_array(iamarray(v));
     2495            }
     2496            break;
     2497        }
    23062498
    23072499        case XC( OC_NEWSOURCE ):
    2308             g_progname = op->l.s;
     2500            g_progname = op->l.new_progname;
    23092501            break;
    23102502
     
    23222514
    23232515        case XC( OC_EXIT ):
    2324             awk_exit(L.d);
     2516            awk_exit(L_d);
    23252517
    23262518        /* -- recursive node type -- */
     
    23332525
    23342526        case XC( OC_FNARG ):
    2335             L.v = &fnargs[op->l.i];
     2527            L.v = &fnargs[op->l.aidx];
    23362528 v_cont:
    23372529            res = op->r.n ? findvar(iamarray(L.v), R.s) : L.v;
     
    23502542            op1 = op->r.n;
    23512543 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            }
    23562551            break;
    23572552
    23582553        case XC( OC_MOVE ):
     2554            debug_printf_eval("MOVE\n");
    23592555            /* 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 {
    23642562                res = copyvar(L.v, R.v);
    2365             }
     2563//          }
    23662564            break;
    23672565
     
    23722570            break;
    23732571
    2374         case XC( OC_FUNC ):
     2572        case XC( OC_FUNC ): {
     2573            var *vbeg, *v;
     2574            const char *sv_progname;
     2575
    23752576            if (!op->r.f->body.first)
    23762577                syntax_error(EMSG_UNDEF_FUNC);
    23772578
    2378             X.v = R.v = nvalloc(op->r.f->nargs+1);
     2579            vbeg = v = nvalloc(op->r.f->nargs + 1);
    23792580            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)
    23852586                    break;
    23862587            }
    23872588
    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
    23922593            res = evaluate(op->r.f->body.first, res);
    2393             g_progname = L.s;
    2394 
     2594
     2595            g_progname = sv_progname;
    23952596            nvfree(fnargs);
    2396             fnargs = R.v;
    2397             break;
     2597            fnargs = v;
     2598
     2599            break;
     2600        }
    23982601
    23992602        case XC( OC_GETLINE ):
    2400         case XC( OC_PGETLINE ):
     2603        case XC( OC_PGETLINE ): {
     2604            rstream *rsm;
     2605            int i;
     2606
    24012607            if (op1) {
    2402                 X.rsm = newfile(L.s);
    2403                 if (!X.rsm->F) {
     2608                rsm = newfile(L.s);
     2609                if (!rsm->F) {
    24042610                    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;
    24072613                    } else {
    2408                         X.rsm->F = fopen(L.s, "r");     /* not xfopen! */
     2614                        rsm->F = fopen_for_read(L.s);  /* not xfopen! */
    24092615                    }
    24102616                }
    24112617            } 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) {
    24172624                setvar_i(intvar[ERRNO], errno);
    24182625                setvar_i(res, -1);
     
    24232630                R.v = intvar[F0];
    24242631
    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;
    24302658                }
    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_MATH
    2447             case F_co:
    2448                 R.d = cos(L.d);
    2449                 break;
    24502659
    24512660            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                }
    24542665
    24552666            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                }
    24582671
    24592672            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                }
    24622677
    24632678            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
    24722684                syntax_error(EMSG_NO_MATH);
    24732685                break;
    2474 #endif
     2686
    24752687            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);
    24782690                srand(seed);
    24792691                break;
    24802692
    24812693            case F_ti:
    2482                 R.d = time(NULL);
     2694                R_d = time(NULL);
    24832695                break;
    24842696
     
    24862698                if (!op1)
    24872699                    L.s = getvar_s(intvar[F0]);
    2488                 R.d = strlen(L.s);
     2700                R_d = strlen(L.s);
    24892701                break;
    24902702
    24912703            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)
    24942706                        ? (system(L.s) >> 8) : 0;
    24952707                break;
    24962708
    24972709            case F_ff:
    2498                 if (!op1)
     2710                if (!op1) {
    24992711                    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();
    25072717                }
    25082718                break;
    25092719
    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);
    25152736                    hash_remove(fdhash, L.s);
    25162737                }
    2517                 if (R.i != 0)
     2738                if (err)
    25182739                    setvar_i(intvar[ERRNO], errno);
    2519                 R.d = (double)R.i;
     2740                R_d = (double)err;
    25202741                break;
    25212742            }
    2522             setvar_i(res, R.d);
    2523             break;
     2743            } /* switch */
     2744            setvar_i(res, R_d);
     2745            break;
     2746        }
    25242747
    25252748        case XC( OC_BUILTIN ):
     
    25312754            break;
    25322755
    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);
    25362760            switch (opn) {
    25372761            case 'P':
    2538                 L.d = ++R.d;
     2762                Ld = ++R_d;
    25392763                goto r_op_change;
    25402764            case 'p':
    2541                 R.d++;
     2765                R_d++;
    25422766                goto r_op_change;
    25432767            case 'M':
    2544                 L.d = --R.d;
     2768                Ld = --R_d;
    25452769                goto r_op_change;
    25462770            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;
    25492775            case '!':
    2550                 L.d = istrue(X.v) ? 0 : 1;
     2776                Ld = !istrue(R.v);
    25512777                break;
    25522778            case '-':
    2553                 L.d = -R.d;
     2779                Ld = -R_d;
    25542780                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) {
    25642789                res = intvar[F0];
    25652790            } else {
    25662791                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        }
    25722798
    25732799        /* concatenation (" ") and index joining (",") */
    25742800        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        }
    25872808
    25882809        case XC( OC_LAND ):
     
    25952816
    25962817        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);
    25992821            switch (opn) {
    26002822            case '+':
    2601                 L.d += R.d;
     2823                L_d += R_d;
    26022824                break;
    26032825            case '-':
    2604                 L.d -= R.d;
     2826                L_d -= R_d;
    26052827                break;
    26062828            case '*':
    2607                 L.d *= R.d;
     2829                L_d *= R_d;
    26082830                break;
    26092831            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;
    26122835                break;
    26132836            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);
    26192841                break;
    26202842            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;
    26232846                break;
    26242847            }
    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
    26292857            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);
    26312859            } 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);
    26352863            }
    26362864            switch (opn & 0xfe) {
    26372865            case 0:
    2638                 R.i = (L.d > 0);
     2866                i = (Ld > 0);
    26392867                break;
    26402868            case 2:
    2641                 R.i = (L.d >= 0);
     2869                i = (Ld >= 0);
    26422870                break;
    26432871            case 4:
    2644                 R.i = (L.d == 0);
     2872                i = (Ld == 0);
    26452873                break;
    26462874            }
    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        }
    26492878
    26502879        default:
     
    26572886        if (nextrec)
    26582887            break;
    2659     }
     2888    } /* while (op) */
     2889
    26602890    nvfree(v1);
     2891    debug_printf_eval("returning from %s(): %p\n", __func__, res);
    26612892    return res;
    26622893#undef fnargs
     
    26992930static int is_assignment(const char *expr)
    27002931{
    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    }
    27022937
    27032938    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);
    27162947    free(exprc);
    27172948    return TRUE;
     
    27272958    const char *fname, *ind;
    27282959
    2729     if (rsm.F) fclose(rsm.F);
     2960    if (rsm.F)
     2961        fclose(rsm.F);
    27302962    rsm.F = NULL;
    27312963    rsm.pos = rsm.adv = 0;
     
    27412973            fname = getvar_s(findvar(iamarray(intvar[ARGV]), ind));
    27422974            if (fname && *fname && !is_assignment(fname))
    2743                 F = afopen(fname, "r");
     2975                F = xfopen_stdin(fname);
    27442976        }
    27452977    } while (!F);
     
    27532985}
    27542986
    2755 int awk_main(int argc, char **argv);
     2987int awk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
    27562988int awk_main(int argc, char **argv)
    27572989{
    27582990    unsigned opt;
    27592991    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;
    27622995    var *v;
    27632996    var tv;
     
    28173050        }
    28183051    }
    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);
    28213054    argv += optind;
    28223055    argc -= optind;
    28233056    if (opt & 0x1)
    28243057        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)))
    28273060            bb_show_usage();
    28283061    }
    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 */
    28393070            for (i = j = 1; j > 0; i += j) {
    28403071                s = xrealloc(s, i + 4096);
    28413072                j = fread(s + i, 1, 4094, from_file);
    28423073            }
    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++;
    28483080    } else { // no -f: take program from 1st parameter
    28493081        if (!argc)
     
    28513083        g_progname = "cmd. line";
    28523084        parse_program(*argv++);
    2853         argc--;
    28543085    }
    28553086    if (opt & 0x8) // -W
     
    28573088
    28583089    /* fill in ARGV array */
    2859     setvar_i(intvar[ARGC], argc + 1);
     3090    setvar_i(intvar[ARGC], argc);
    28603091    setari_u(intvar[ARGV], 0, "awk");
    28613092    i = 0;
     
    28683099
    28693100    /* input file could already be opened in BEGIN block */
    2870     if (!iF) iF = next_input_file();
     3101    if (!iF)
     3102        iF = next_input_file();
    28713103
    28723104    /* passing through input files */
  • branches/2.2.9/mindi-busybox/editors/cmp.c

    r1765 r2725  
    55 * Copyright (C) 2000,2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
    66 *
    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.
    88 */
    99
     
    1111/* http://www.opengroup.org/onlinepubs/007904975/utilities/cmp.html */
    1212
    13 /* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
    14  *
    15  * Original version majorly reworked for SUSv3 compliance, bug fixes, and
    16  * 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 format
    21  *    in the '-l' case.
    22  */
    23 
    2413#include "libbb.h"
    2514
    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 
    3615static 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";
     16static 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"
     18static const char fmt_l_opt[] ALIGN1 = "%.0s%.0s%"OFF_FMT"u %3o %3o\n";
    4019
    4120static const char opt_chars[] ALIGN1 = "sl";
     
    4322#define CMP_OPT_l (1<<1)
    4423
    45 int cmp_main(int argc, char **argv);
    46 int cmp_main(int argc, char **argv)
     24int cmp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     25int cmp_main(int argc UNUSED_PARAM, char **argv)
    4726{
    4827    FILE *fp1, *fp2, *outfile = stdout;
    4928    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;
    5230    int line_pos = 1; /* Hopefully won't overflow... */
    5331    const char *fmt;
     
    5634    int retval = 0;
    5735
    58     xfunc_error_retval = 2; /* 1 is returned if files are different. */
    59 
    6036    opt_complementary = "-1"
    61             USE_DESKTOP(":?4")
    62             SKIP_DESKTOP(":?2")
     37            IF_DESKTOP(":?4")
     38            IF_NOT_DESKTOP(":?2")
    6339            ":l--s:s--l";
    6440    opt = getopt32(argv, opt_chars);
     
    6642
    6743    filename1 = *argv;
    68     fp1 = cmp_xfopen_input(filename1);
    69 
    7044    if (*++argv) {
    7145        filename2 = *argv;
    72 #if ENABLE_DESKTOP
    73         if (*++argv) {
     46        if (ENABLE_DESKTOP && *++argv) {
    7447            skip1 = XATOOFF(*argv);
    7548            if (*++argv) {
     
    7750            }
    7851        }
    79 #endif
    8052    }
    8153
    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);
    8359    if (fp1 == fp2) {       /* Paranoia check... stdin == stdin? */
    8460        /* Note that we don't bother reading stdin.  Neither does gnu wc.
     
    8864        return 0;
    8965    }
     66    logmode = LOGMODE_STDIO;
    9067
    9168    if (opt & CMP_OPT_l)
     
    9471        fmt = fmt_differ;
    9572
    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    }
    10077    do {
    10178        c1 = getc(fp1);
     
    11996                /* There may have been output to stdout (option -l), so
    12097                 * make sure we fflush before writing to stderr. */
    121                 xfflush_stdout();
     98                fflush_all();
    12299            }
    123100            if (!(opt & CMP_OPT_s)) {
  • branches/2.2.9/mindi-busybox/editors/diff.c

    r1765 r2725  
    33 * Mini diff implementation for busybox, adapted from OpenBSD diff.
    44 *
     5 * Copyright (C) 2010 by Matheus Izvekov <mizvekov@gmail.com>
    56 * Copyright (C) 2006 by Robert Sullivan <cogito.ergo.cogito@hotmail.com>
    67 * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
     
    1011 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
    1112 *
    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.
    1314 */
    14 
    15 #include "libbb.h"
    16 
    17 #define FSIZE_MAX 32768
    18 
    19 /*
    20  * Output flags
    21  */
    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 values
    28  * Guide:
    29  * D_SAME - files are the same
    30  * D_DIFFER - files differ
    31  * D_BINARY - binary files differ
    32  * D_COMMON - subdirectory common to both dirs
    33  * D_ONLY - file only exists in one dir
    34  * D_MISMATCH1 - path1 a dir, path2 a file
    35  * D_MISMATCH2 - path1 a file, path2 a dir
    36  * D_ERROR - error occurred
    37  * D_SKIPPED1 - skipped path1 as it is a special file
    38  * D_SKIPPED2 - skipped path2 as it is a special file
    39  */
    40 
    41 #define D_SAME      0
    42 #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_bufsiz1
    69 
    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 information
    83  * doing a "context" or "unified" diff.  (see routine "change" to
    84  * 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_size
    281      || (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         else
    356             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         else
    425             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_MINIMAL
    438     const unsigned int bound =
    439         (option_mask32 & FLAG_d) ? UINT_MAX : MAX(256, isqrt(n));
    440 #else
    441     const unsigned int bound = MAX(256, isqrt(n));
    442 #endif
    443     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 due
    516  *      to confounding by hashing (which result in "jackpot")
    517  *  2.  collect random access indexes to the two files
    518  */
    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 newline
    546                  * 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     else
    651         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_BINARY
    691     int i, cnt;
    692 #endif
    693 
    694     if ((option_mask32 & FLAG_a) || f == NULL)
    695         return 1;
    696 
    697 #if ENABLE_FEATURE_DIFF_BINARY
    698     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 #endif
    707     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 lines
    736      * 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 changes
    746          * d: only changes in the old file
    747          * a: only changes in the new file
    748          */
    749         if (a <= b && c <= d)
    750             ch = 'c';
    751         else
    752             ch = (a <= b) ? 'd' : 'a';
    753 #if 0
    754         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 #else
    770         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 #endif
    779         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     else
    793         printf("--- %s\t%s", file1, ctime(&stb1.st_mtime));
    794     if (label2)
    795         printf("+++ %s\n", label2);
    796     else
    797         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 file
    803  * to get to lines c to d of the to file.  If a is greater than b then there
    804  * are no lines in the from file involved and this means that there were
    805  * lines appended (beginning at b).  If c is greater than d then there are
    806  * 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 the
    837          * 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 }
    87915
    88016/*
     
    88521 * The major goal is to generate the match vector J.
    88622 * J[i] is the index of the line in file1 corresponding
    887  * to line i file0. J[i] = 0 if there is no
     23 * to line i in file0. J[i] = 0 if there is no
    88824 * such line in file1.
    88925 *
    89026 * Lines are hashed so as to work in core. All potential
    89127 * matches are located by sorting the lines of each file
    892  * on the hash (called ``value''). In particular, this
     28 * on the hash (called "value"). In particular, this
    89329 * collects the equivalence classes in file1 together.
    89430 * Subroutine equiv replaces the value of each line in
     
    90642 * through the lines of file0, developing a vector klist
    90743 * 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 that
     44 * pair of lines x,y (x in file0, y in file1) such that
    90945 * there is a common subsequence of length k
    91046 * between the first i lines of file0 and the first y
     
    93773 * The core requirements for problems larger than somewhat
    93874 * are (in words) 2*length(file0) + length(file1) +
    939  * 3*(number of k-candidates installed),  typically about
     75 * 3*(number of k-candidates installed), typically about
    94076 * 6n words for files of length n.
    94177 */
    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
     87enum {                  /* 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
     93enum {                  /* 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 */
     116typedef struct FILE_and_pos_t {
     117    FILE *ft_fp;
     118    off_t ft_pos;
     119} FILE_and_pos_t;
     120
     121struct 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
     138typedef int token_t;
     139
     140enum {
     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
     156static 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 */
     167static 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
     223struct cand {
     224    int x;
     225    int y;
     226    int pred;
     227};
     228
     229static 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
     251static 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
     262static 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
     317struct 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
     328static 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
     354static void unsort(const struct line *f, int l, int *b)
     355{
    948356    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
     365static 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
     377static 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 */
     416static 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            }
     463start:
     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]));
    999501    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);
    1011531    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
     562static 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
     673static 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;
     728out:
     729    fclose_if_not_stdin(fp[0]);
     730    fclose_if_not_stdin(fp[1]);
     731
     732    return status;
     733}
     734
     735static 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}
    1039749
    1040750#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 
     751struct dlist {
     752    size_t len;
     753    int s, e;
     754    char **dl;
     755};
    1100756
    1101757/* 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++;
     758static 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++;
    1110769    return TRUE;
    1111770}
    1112771
    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 */
     775static 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
     805static 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    }
    1180834    /* Now that both dirlist1 and dirlist2 contain sorted directory
    1181835     * listings, we can start to go through dirlist1. If both listings
    1182836     * contain the same file, then do a normal diff. Otherwise, behaviour
    1183837     * 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++;
    1188885        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);
    1205893    }
    1206894}
    1207895#endif
    1208896
    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
     898static 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
     919int diff_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     920int diff_main(int argc UNUSED_PARAM, char **argv)
     921{
     922    int gotstdin = 0, i;
     923    char *file[2], *s_start = NULL;
    1216924    llist_t *L_arg = NULL;
    1217925
    1218926    INIT_G();
    1219927
    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);
    1226935    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)) {
    1262953#if ENABLE_FEATURE_DIFF_DIR
    1263         diffdir(f1, f2);
     954        diffdir(file, s_start);
    1264955#else
    1265         bb_error_msg_and_die("directory comparison not supported");
     956        bb_error_msg_and_die("no support for directory comparison");
    1266957#endif
    1267958    } 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  
    99
    1010#include "libbb.h"
    11 
    12 #define searchString bb_common_bufsiz1
    13 
    14 enum {
    15     USERSIZE = sizeof(searchString) > 1024 ? 1024
    16              : sizeof(searchString) - 1, /* max line length typed in by user */
    17     INITBUF_SIZE = 1024, /* initial buffer size */
    18 };
    1911
    2012typedef struct LINE {
     
    2517} LINE;
    2618
    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
     22enum {
     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
     28struct 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
    3157
    3258static void doCommands(void);
    3359static void subCommand(const char *cmd, int num1, int num2);
    34 static int getNum(const char **retcp, int *retHaveNum, int *retNum);
     60static int getNum(const char **retcp, smallint *retHaveNum, int *retNum);
    3561static int setCurNum(int num);
    36 static int initEdit(void);
    37 static void termEdit(void);
    3862static void addLines(int num);
    3963static int insertLine(int num, const char *data, int len);
    40 static int deleteLines(int num1, int num2);
     64static void deleteLines(int num1, int num2);
    4165static int printLines(int num1, int num2, int expandFlag);
    4266static int writeLines(const char *file, int num1, int num2);
     
    4468static int searchLines(const char *str, int num1, int num2);
    4569static LINE *findLine(int num);
    46 
    4770static int findString(const LINE *lp, const char * str, int len, int offset);
    4871
    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
     73static 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
     83static char *skip_blank(const char *cp)
     84{
     85    while (isblank(*cp))
     86        cp++;
     87    return (char *)cp;
     88}
     89
     90
     91int ed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     92int 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)) {
    61105            return EXIT_SUCCESS;
    62106        }
    63 
    64         if (!readLines(fileName, 1)) {
    65             termEdit();
    66             return EXIT_SUCCESS;
    67         }
    68 
    69107        if (lastNum)
    70108            setCurNum(1);
    71 
    72109        dirty = FALSE;
    73110    }
    74111
    75112    doCommands();
    76 
    77     termEdit();
    78113    return EXIT_SUCCESS;
    79114}
     
    85120{
    86121    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;
    89125
    90126    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)
    95134            return;
    96 
    97         len = strlen(buf);
    98 
    99         if (len == 0)
    100             return;
    101 
    102135        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 
    114136        while ((endbuf > buf) && isblank(endbuf[-1]))
    115137            endbuf--;
    116 
    117138        *endbuf = '\0';
    118139
    119         cp = buf;
    120 
    121         while (isblank(*cp))
    122             cp++;
    123 
     140        cp = skip_blank(buf);
    124141        have1 = FALSE;
    125142        have2 = FALSE;
     
    133150            continue;
    134151
    135         while (isblank(*cp))
    136             cp++;
     152        cp = skip_blank(cp);
    137153
    138154        if (*cp == ',') {
    139155            cp++;
    140 
    141156            if (!getNum(&cp, &have2, &num2))
    142157                continue;
    143 
    144158            if (!have1)
    145159                num1 = 1;
    146 
    147160            if (!have2)
    148161                num2 = lastNum;
    149 
    150162            have1 = TRUE;
    151163            have2 = TRUE;
    152164        }
    153 
    154165        if (!have1)
    155166            num1 = curNum;
    156 
    157167        if (!have2)
    158168            num2 = num1;
    159169
    160170        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') {
    198191                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;
    323285            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))
    329304                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;
    356322        }
    357323    }
     
    363329 * The current line is set to the last substitution done.
    364330 */
    365 static void subCommand(const char * cmd, int num1, int num2)
     331static void subCommand(const char *cmd, int num1, int num2)
    366332{
    367333    char *cp, *oldStr, *newStr, buf[USERSIZE];
     
    370336    int globalFlag, printFlag, didSub, needPrint;
    371337
    372     if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
    373         bb_error_msg("bad line range for substitute");
     338    if (bad_nums(num1, num2, "substitute"))
    374339        return;
    375     }
    376340
    377341    globalFlag = FALSE;
     
    395359
    396360    cp = strchr(cp, delim);
    397 
    398361    if (cp == NULL) {
    399362        bb_error_msg("missing 2nd delimiter for substitute");
     
    415378            globalFlag = TRUE;
    416379            break;
    417 
    418380        case 'p':
    419381            printFlag = TRUE;
    420382            break;
    421 
    422383        default:
    423384            bb_error_msg("unknown option for substitute");
     
    430391            return;
    431392        }
    432 
    433393        oldStr = searchString;
    434394    }
     
    438398
    439399    lp = findLine(num1);
    440 
    441400    if (lp == NULL)
    442401        return;
     
    456415                needPrint = FALSE;
    457416            }
    458 
    459417            offset = 0;
    460418            lp = lp->next;
    461419            num1++;
    462 
    463420            continue;
    464421        }
     
    474431        if (deltaLen <= 0) {
    475432            memcpy(&lp->data[offset], newStr, newLen);
    476 
    477433            if (deltaLen) {
    478434                memcpy(&lp->data[offset + newLen],
     
    482438                lp->len += deltaLen;
    483439            }
    484 
    485440            offset += newLen;
    486 
    487441            if (globalFlag)
    488442                continue;
    489 
    490443            if (needPrint) {
    491444                printLines(num1, num1, FALSE);
    492445                needPrint = FALSE;
    493446            }
    494 
    495447            lp = lp->next;
    496448            num1++;
    497 
    498449            continue;
    499450        }
     
    504455         * the old line structure.
    505456         */
    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);
    512458
    513459        nlp->len = lp->len + deltaLen;
    514460
    515461        memcpy(nlp->data, lp->data, offset);
    516 
    517462        memcpy(&nlp->data[offset], newStr, newLen);
    518 
    519463        memcpy(&nlp->data[offset + newLen],
    520464            &lp->data[offset + oldLen],
     
    555499 * offset in the line.  Returns the offset of the found string, or -1.
    556500 */
    557 static int findString( const LINE * lp, const char * str, int len, int offset)
     501static int findString(const LINE *lp, const char *str, int len, int offset)
    558502{
    559503    int left;
     
    565509    while (left >= len) {
    566510        ncp = memchr(cp, *str, left);
    567 
    568511        if (ncp == NULL)
    569512            return -1;
    570 
    571513        left -= (ncp - cp);
    572 
    573514        if (left < len)
    574515            return -1;
    575 
    576516        cp = ncp;
    577 
    578517        if (memcmp(cp, str, len) == 0)
    579518            return (cp - lp->data);
    580 
    581519        cp++;
    582520        left--;
     
    598536    char buf[USERSIZE + 1];
    599537
    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        }
    601550        if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0'))
    602551            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 
    617552        if (!insertLine(num++, buf, len))
    618553            return;
     
    629564 * The character pointer which stopped the scan is also returned.
    630565 */
    631 static int getNum(const char **retcp, int *retHaveNum, int *retNum)
     566static int getNum(const char **retcp, smallint *retHaveNum, int *retNum)
    632567{
    633568    const char *cp;
    634569    char *endStr, str[USERSIZE];
    635     int haveNum, value, num, sign;
     570    int value, num;
     571    smallint haveNum, minus;
    636572
    637573    cp = *retcp;
     574    value = 0;
    638575    haveNum = FALSE;
    639     value = 0;
    640     sign = 1;
     576    minus = 0;
    641577
    642578    while (TRUE) {
    643         while (isblank(*cp))
    644             cp++;
     579        cp = skip_blank(cp);
    645580
    646581        switch (*cp) {
     
    659594            case '\'':
    660595                cp++;
    661 
    662596                if ((*cp < 'a') || (*cp > 'z')) {
    663597                    bb_error_msg("bad mark name");
    664598                    return FALSE;
    665599                }
    666 
    667600                haveNum = TRUE;
    668601                num = marks[*cp++ - 'a'];
     
    672605                strcpy(str, ++cp);
    673606                endStr = strchr(str, '/');
    674 
    675607                if (endStr) {
    676608                    *endStr++ = '\0';
    677609                    cp += (endStr - str);
    678                 }
    679                 else
     610                } else
    680611                    cp = "";
    681 
    682612                num = searchLines(str, curNum, lastNum);
    683 
    684613                if (num == 0)
    685614                    return FALSE;
    686 
    687615                haveNum = TRUE;
    688616                break;
     
    695623                    return TRUE;
    696624                }
    697 
    698625                num = 0;
    699 
    700626                while (isdigit(*cp))
    701627                    num = num * 10 + *cp++ - '0';
    702 
    703628                haveNum = TRUE;
    704629                break;
    705630        }
    706631
    707         value += num * sign;
    708 
    709         while (isblank(*cp))
    710             cp++;
     632        value += (minus ? -num : num);
     633
     634        cp = skip_blank(cp);
    711635
    712636        switch (*cp) {
    713637            case '-':
    714                 sign = -1;
     638                minus = 1;
    715639                cp++;
    716640                break;
    717641
    718642            case '+':
    719                 sign = 1;
     643                minus = 0;
    720644                cp++;
    721645                break;
     
    732656
    733657/*
    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 /*
    798658 * Read lines from a file at the specified line number.
    799659 * Returns TRUE if the file was successfully read.
    800660 */
    801 static int readLines(const char * file, int num)
     661static int readLines(const char *file, int num)
    802662{
    803663    int fd, cc;
     
    811671
    812672    fd = open(file, 0);
    813 
    814673    if (fd < 0) {
    815         perror(file);
     674        bb_simple_perror_msg(file);
    816675        return FALSE;
    817676    }
     
    824683
    825684    printf("\"%s\", ", file);
    826     fflush(stdout);
     685    fflush_all();
    827686
    828687    do {
     
    831690        if (cp) {
    832691            len = (cp - bufPtr) + 1;
    833 
    834692            if (!insertLine(num, bufPtr, len)) {
    835693                close(fd);
    836694                return FALSE;
    837695            }
    838 
    839696            bufPtr += len;
    840697            bufUsed -= len;
     
    842699            lineCount++;
    843700            num++;
    844 
    845701            continue;
    846702        }
     
    853709        if (bufUsed >= bufSize) {
    854710            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);
    863712            bufBase = cp;
    864713            bufPtr = bufBase + bufUsed;
     
    866715        }
    867716
    868         cc = read(fd, bufPtr, bufSize - bufUsed);
     717        cc = safe_read(fd, bufPtr, bufSize - bufUsed);
    869718        bufUsed += cc;
    870719        bufPtr = bufBase;
     
    873722
    874723    if (cc < 0) {
    875         perror(file);
     724        bb_simple_perror_msg(file);
    876725        close(fd);
    877726        return FALSE;
     
    883732            return -1;
    884733        }
    885 
    886734        lineCount++;
    887735        charCount += bufUsed;
     
    901749 * Returns TRUE if successful, or FALSE on an error with a message output.
    902750 */
    903 static int writeLines(const char * file, int num1, int num2)
     751static int writeLines(const char *file, int num1, int num2)
    904752{
    905753    LINE *lp;
    906754    int fd, lineCount, charCount;
    907755
    908     if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
    909         bb_error_msg("bad line range for write");
     756    if (bad_nums(num1, num2, "write"))
    910757        return FALSE;
    911     }
    912758
    913759    lineCount = 0;
     
    915761
    916762    fd = creat(file, 0666);
    917 
    918763    if (fd < 0) {
    919         perror(file);
     764        bb_simple_perror_msg(file);
    920765        return FALSE;
    921766    }
    922767
    923768    printf("\"%s\", ", file);
    924     fflush(stdout);
     769    fflush_all();
    925770
    926771    lp = findLine(num1);
    927 
    928772    if (lp == NULL) {
    929773        close(fd);
     
    932776
    933777    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);
    936780            close(fd);
    937781            return FALSE;
    938782        }
    939 
    940783        charCount += lp->len;
    941784        lineCount++;
     
    944787
    945788    if (close(fd) < 0) {
    946         perror(file);
     789        bb_simple_perror_msg(file);
    947790        return FALSE;
    948791    }
     
    965808    int ch, count;
    966809
    967     if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
    968         bb_error_msg("bad line range for print");
     810    if (bad_nums(num1, num2, "print"))
    969811        return FALSE;
    970     }
    971812
    972813    lp = findLine(num1);
    973 
    974814    if (lp == NULL)
    975815        return FALSE;
     
    977817    while (num1 <= num2) {
    978818        if (!expandFlag) {
    979             write(1, lp->data, lp->len);
     819            write(STDOUT_FILENO, lp->data, lp->len);
    980820            setCurNum(num1++);
    981821            lp = lp->next;
    982 
    983822            continue;
    984823        }
     
    995834
    996835        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);
    1015838        }
    1016839
     
    1032855 * Returns TRUE if successful.
    1033856 */
    1034 static int insertLine(int num, const char * data, int len)
     857static int insertLine(int num, const char *data, int len)
    1035858{
    1036859    LINE *newLp, *lp;
     
    1041864    }
    1042865
    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);
    1049867
    1050868    memcpy(newLp->data, data, len);
     
    1055873    else {
    1056874        lp = findLine(num);
    1057 
    1058875        if (lp == NULL) {
    1059876            free((char *) newLp);
     
    1076893 * Delete lines from the given range.
    1077894 */
    1078 static int deleteLines(int num1, int num2)
     895static void deleteLines(int num1, int num2)
    1079896{
    1080897    LINE *lp, *nlp, *plp;
    1081898    int count;
    1082899
    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;
    1087902
    1088903    lp = findLine(num1);
    1089 
    1090904    if (lp == NULL)
    1091         return FALSE;
     905        return;
    1092906
    1093907    if ((curNum >= num1) && (curNum <= num2)) {
     
    1101915
    1102916    count = num2 - num1 + 1;
    1103 
    1104917    if (curNum > num2)
    1105918        curNum -= count;
    1106 
    1107919    lastNum -= count;
    1108920
     
    1112924        plp->next = nlp;
    1113925        nlp->prev = plp;
    1114         lp->next = NULL;
    1115         lp->prev = NULL;
    1116         lp->len = 0;
    1117926        free(lp);
    1118927        lp = nlp;
     
    1120929
    1121930    dirty = TRUE;
    1122 
    1123     return TRUE;
    1124931}
    1125932
     
    1127934/*
    1128935 * Search for a line which contains the specified string.
    1129  * If the string is NULL, then the previously searched for string
     936 * If the string is "", then the previously searched for string
    1130937 * is used.  The currently searched for string is saved for future use.
    1131938 * Returns the line number which matches, or 0 if there was no match
    1132939 * with an error printed.
    1133940 */
    1134 static int searchLines(const char *str, int num1, int num2)
     941static NOINLINE int searchLines(const char *str, int num1, int num2)
    1135942{
    1136943    const LINE *lp;
    1137944    int len;
    1138945
    1139     if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
    1140         bb_error_msg("bad line numbers for search");
     946    if (bad_nums(num1, num2, "search"))
    1141947        return 0;
    1142     }
    1143948
    1144949    if (*str == '\0') {
     
    1147952            return 0;
    1148953        }
    1149 
    1150954        str = searchString;
    1151955    }
     
    1157961
    1158962    lp = findLine(num1);
    1159 
    1160963    if (lp == NULL)
    1161964        return 0;
     
    1164967        if (findString(lp, str, len, 0) >= 0)
    1165968            return num1;
    1166 
    1167969        num1++;
    1168970        lp = lp->next;
    1169971    }
    1170972
    1171     bb_error_msg("cannot find string \"%s\"", str);
     973    bb_error_msg("can't find string \"%s\"", str);
    1172974    return 0;
    1173975}
     
    1197999    lp = curLine;
    11981000    lnum = curNum;
    1199 
    12001001    if (num < (curNum / 2)) {
    12011002        lp = lines.next;
    12021003        lnum = 1;
    1203     }
    1204     else if (num > ((curNum + lastNum) / 2)) {
     1004    } else if (num > ((curNum + lastNum) / 2)) {
    12051005        lp = lines.prev;
    12061006        lnum = lastNum;
     
    12291029
    12301030    lp = findLine(num);
    1231 
    12321031    if (lp == NULL)
    12331032        return FALSE;
    1234 
    12351033    curNum = num;
    12361034    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:
    52 *
    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.
    75 *
    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>
    117 *
    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"?)
    1510 *
    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
    2022 */
    2123
    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"
    2356
    2457#include "libbb.h"
    2558
    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
     62struct 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.
     70static
     71void 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
     83static
     84struct 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
     105struct 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
     145static 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
     159static 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
     182static 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
     214static 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                }
    146258            }
    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;
    234311                    break;
    235312                }
    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;
    237322            }
    238             if (hunk_error) {
    239                 bad_hunk_count++;
     323        }
     324    }
     325out:
     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;
     331done:
     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
     348int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     349int 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;
    240405            }
    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;
    256419            }
    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                }
    262435            }
    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                }
    268525            }
    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  
    66 * Copyright (C) 1999,2000,2001 by Mark Whitley <markw@codepoet.org>
    77 * Copyright (C) 2002  Matt Kraai
    8  * Copyright (C) 2003 by Glenn McGrath <bug1@iinet.net.au>
     8 * Copyright (C) 2003 by Glenn McGrath
    99 * Copyright (C) 2003,2004 by Rob Landley <rob@landley.net>
    1010 *
    1111 * MAINTAINER: Rob Landley <rob@landley.net>
    1212 *
    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.
    1414 */
    1515
     
    2424  (G.sed_cmd_head/G.sed_cmd_tail).
    2525
    26   add_input_file() adds a FILE * to the list of input files.  We need to
     26  add_input_file() adds a FILE* to the list of input files.  We need to
    2727  know all input sources ahead of time to find the last line for the $ match.
    2828
     
    6262#include "xregex.h"
    6363
     64enum {
     65    OPT_in_place = 1 << 0,
     66};
     67
    6468/* Each sed command turns into one of these structures. */
    6569typedef struct sed_cmd_s {
     
    7781    char *string;           /* Data string for (saicytb) commands. */
    7882
    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) */
    8084
    8185    /* Bitfields (gcc won't group them if we don't) */
     
    114118
    115119    struct pipeline {
    116         char *buf;  /* Space to hold string */
    117         int idx;    /* Space used */
    118         int len;    /* Space allocated */
     120        char *buf;  /* Space to hold string */
     121        int idx;    /* Space used */
     122        int len;    /* Space allocated */
    119123    } pipeline;
     124} FIX_ALIASING;
     125#define G (*(struct globals*)&bb_common_bufsiz1)
     126struct BUG_G_too_big {
     127        char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
    120128};
    121 #define G (*(struct globals*)&bb_common_bufsiz1)
    122 void BUG_sed_globals_too_big(void);
    123129#define INIT_G() do { \
    124     if (sizeof(struct globals) > COMMON_BUFSIZE) \
    125         BUG_sed_globals_too_big(); \
    126130    G.sed_cmd_tail = &G.sed_cmd_head; \
    127131} while (0)
     
    158162    }
    159163
    160     if (G.hold_space) free(G.hold_space);
     164    free(G.hold_space);
    161165
    162166    while (G.current_input_file < G.input_file_count)
     
    174178}
    175179
    176 /* strdup, replacing "\n" with '\n', and "\delimiter" with 'delimiter' */
     180/* strcpy, replacing "\from" with 'to'. If to is NUL, replacing "\any" with 'any' */
    177181
    178182static void parse_escapes(char *dest, const char *string, int len, char from, char to)
     
    189193            *dest++ = string[i++];
    190194        }
     195        /* TODO: is it safe wrt a string with trailing '\\' ? */
    191196        *dest++ = string[i++];
    192197    }
    193     *dest = 0;
     198    *dest = '\0';
    194199}
    195200
     
    199204
    200205    parse_escapes(dest, string, len, 'n', '\n');
     206    /* GNU sed also recognizes \t */
     207    parse_escapes(dest, dest, strlen(dest), 't', '\t');
    201208    return dest;
    202209}
     
    206213 * index_of_next_unescaped_regexp_delim - walks left to right through a string
    207214 * beginning at a specified index and returns the index of the next regular
    208  * expression delimiter (typically a forward * slash ('/')) not preceded by
     215 * expression delimiter (typically a forward slash ('/')) not preceded by
    209216 * a backslash ('\').  A negative delimiter disables square bracket checking.
    210217 */
     
    299306
    300307/* 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)
     308static int parse_file_cmd(/*sed_cmd_t *sed_cmd,*/ const char *filecmdstr, char **retval)
    302309{
    303310    int start = 0, idx, hack = 0;
     
    351358                const char *pos = substr + idx;
    352359/* 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);
    354361                idx = pos - substr;
    355362            }
     
    357364        }
    358365        /* Skip spaces */
    359         if (isspace(substr[idx])) continue;
     366        if (isspace(substr[idx]))
     367            continue;
    360368
    361369        switch (substr[idx]) {
    362370        /* Replace all occurrences */
    363371        case 'g':
    364             if (match[0] != '^') sed_cmd->which_match = 0;
     372            if (match[0] != '^')
     373                sed_cmd->which_match = 0;
    365374            break;
    366375        /* Print pattern space */
     
    372381        {
    373382            char *temp;
    374             idx += parse_file_cmd(sed_cmd, substr+idx, &temp);
     383            idx += parse_file_cmd(/*sed_cmd,*/ substr+idx, &temp);
    375384            break;
    376385        }
     
    381390        /* Comment */
    382391        case '#':
    383             while (substr[++idx]) /*skip all*/;
     392            // while (substr[++idx]) continue;
     393            idx += strlen(substr + idx); // same
    384394            /* Fall through */
    385395        /* End of command */
     
    391401        }
    392402    }
    393 out:
     403 out:
    394404    /* compile the match string into a regex */
    395405    if (*match != '\0') {
     
    414424    else if (strchr("aic", sed_cmd->cmd)) {
    415425        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");
    418427        for (;;) {
    419428            if (*cmdstr == '\n' || *cmdstr == '\\') {
    420429                cmdstr++;
    421430                break;
    422             } else if (isspace(*cmdstr))
    423                 cmdstr++;
    424             else
     431            }
     432            if (!isspace(*cmdstr))
    425433                break;
     434            cmdstr++;
    426435        }
    427436        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');
    429439        cmdstr += strlen(cmdstr);
    430440    /* handle file cmds: (r)ead */
     
    432442        if (sed_cmd->end_line || sed_cmd->end_match)
    433443            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);
    435445        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);
    437447            sed_cmd->sw_last_char = '\n';
    438448        }
     
    483493{
    484494    sed_cmd_t *sed_cmd;
    485     int temp;
     495    unsigned len, n;
    486496
    487497    /* Append this line to any unfinished line from last time. */
     
    492502    }
    493503
    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 */
    497509        if (!G.add_cmd_line)
    498510            G.add_cmd_line = xstrdup(cmdstr);
    499         G.add_cmd_line[temp] = '\0';
     511        G.add_cmd_line[len-1] = '\0';
    500512        return;
    501513    }
     
    556568        if (!*cmdstr)
    557569            bb_error_msg_and_die("missing command");
    558         sed_cmd->cmd = *(cmdstr++);
     570        sed_cmd->cmd = *cmdstr++;
    559571        cmdstr = parse_cmd_args(sed_cmd, cmdstr);
    560572
     
    585597static void do_subst_w_backrefs(char *line, char *replace)
    586598{
    587     int i,j;
     599    int i, j;
    588600
    589601    /* go through the replacement string */
     
    620632}
    621633
    622 static int do_subst_command(sed_cmd_t *sed_cmd, char **line)
    623 {
    624     char *oldline = *line;
     634static int do_subst_command(sed_cmd_t *sed_cmd, char **line_p)
     635{
     636    char *line = *line_p;
    625637    int altered = 0;
    626     int match_count = 0;
     638    unsigned match_count = 0;
    627639    regex_t *current_regex;
    628640
     641    current_regex = sed_cmd->sub_match;
    629642    /* Handle empty regex. */
    630     if (sed_cmd->sub_match == NULL) {
     643    if (!current_regex) {
    631644        current_regex = G.previous_regex_ptr;
    632645        if (!current_regex)
    633646            bb_error_msg_and_die("no previous regexp");
    634     } else
    635         G.previous_regex_ptr = current_regex = sed_cmd->sub_match;
     647    }
     648    G.previous_regex_ptr = current_regex;
    636649
    637650    /* 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))
    639652        return 0;
    640653
     
    653666           echo "hi" | busybox sed 's/^/!/g' */
    654667        if (!G.regmatch[0].rm_so && !G.regmatch[0].rm_eo && match_count) {
    655             pipe_putc(*oldline++);
     668            pipe_putc(*line++);
    656669            continue;
    657670        }
     
    661674        /* If we aren't interested in this match, output old line to
    662675           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        ) {
    664679            for (i = 0; i < G.regmatch[0].rm_eo; i++)
    665                 pipe_putc(*oldline++);
     680                pipe_putc(*line++);
    666681            continue;
    667682        }
     
    669684        /* print everything before the match */
    670685        for (i = 0; i < G.regmatch[0].rm_so; i++)
    671             pipe_putc(oldline[i]);
     686            pipe_putc(line[i]);
    672687
    673688        /* then print the substitution string */
    674         do_subst_w_backrefs(oldline, sed_cmd->string);
     689        do_subst_w_backrefs(line, sed_cmd->string);
    675690
    676691        /* advance past the match */
    677         oldline += G.regmatch[0].rm_eo;
     692        line += G.regmatch[0].rm_eo;
    678693        /* flag that something has changed */
    679694        altered++;
    680695
    681696        /* 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);
    684702
    685703    /* 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;
    693713    return altered;
    694714}
     
    725745static void add_input_file(FILE *file)
    726746{
    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);
    729748    G.input_file_list[G.input_file_count++] = file;
    730749}
     
    859878    next_line = get_next_line(&next_gets_char);
    860879
    861     /* go through every line in each file */
    862 again:
     880    /* Go through every line in each file */
     881 again:
    863882    substituted = 0;
    864883
    865884    /* Advance to next line.  Stop if out of lines. */
    866885    pattern_space = next_line;
    867     if (!pattern_space) return;
     886    if (!pattern_space)
     887        return;
    868888    last_gets_char = next_gets_char;
    869889
     
    872892    next_line = get_next_line(&next_gets_char);
    873893    linenum++;
    874 restart:
    875     /* for every line, go through all the commands */
     894
     895    /* For every line, go through all the commands */
     896 restart:
    876897    for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
    877898        int old_matched, matched;
     
    880901
    881902        /* 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));
    882915
    883916        /* Are we continuing a previous multi-line match? */
     
    887920                && !sed_cmd->beg_match && !sed_cmd->end_match)
    888921            /* 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                )
    890930            /* Or does this line match our begin address regex? */
    891931            || (beg_match(sed_cmd, pattern_space))
     
    894934
    895935        /* Snapshot the value */
    896 
    897936        matched = sed_cmd->in_match;
    898937
     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
    899941        /* Is this line the end of the current match? */
    900942
    901943        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            }
    902950            sed_cmd->in_match = !(
    903951                /* has the ending line come, or is this a single address command? */
     
    915963        }
    916964
    917         /* Skip blocks of commands we didn't match. */
     965        /* Skip blocks of commands we didn't match */
    918966        if (sed_cmd->cmd == '{') {
    919967            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                    }
    921977                    sed_cmd = sed_cmd->next;
    922978                    if (!sed_cmd)
     
    928984
    929985        /* 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';
    9421013                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))
    9661042                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)
    10361050                puts_maybe_newline(
    10371051                    pattern_space, sed_cmd->sw_file,
    10381052                    &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;
    10781103                last_gets_char = next_gets_char;
    10791104                next_line = get_next_line(&next_gets_char);
     1105                substituted = 0;
    10801106                linenum++;
    10811107                break;
    10821108            }
    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;
    11091168                    }
    11101169                }
    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 */
    11731232
    11741233    /*
    1175      * exit point from sedding...
     1234     * Exit point from sedding...
    11761235     */
    11771236 discard_commands:
     
    12121271            /* Odd number of preceding slashes - newline is escaped */
    12131272            if (slashes & 1) {
    1214                 strcpy(eol-1, eol);
     1273                overlapping_strcpy(eol - 1, eol);
    12151274                eol = strchr(eol, '\n');
    12161275                goto next;
     
    12241283}
    12251284
    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     };
     1285int sed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     1286int sed_main(int argc UNUSED_PARAM, char **argv)
     1287{
    12321288    unsigned opt;
    12331289    llist_t *opt_e, *opt_f;
     
    12401296
    12411297    /* Lie to autoconf when it starts asking stupid questions. */
    1242     if (argc == 2 && !strcmp(argv[1], "--version")) {
     1298    if (argv[1] && !strcmp(argv[1], "--version")) {
    12431299        puts("This is not GNU sed version 4.0");
    12441300        return 0;
     
    12491305    opt_complementary = "e::f::" /* can occur multiple times */
    12501306                        "nn"; /* count -n */
     1307    /* -i must be first, to match OPT_in_place definition */
    12511308    opt = getopt32(argv, "irne:f:", &opt_e, &opt_f,
    12521309                &G.be_quiet); /* counter for -n */
    1253     argc -= optind;
     1310    //argc -= optind;
    12541311    argv += optind;
    12551312    if (opt & OPT_in_place) { // -i
     
    12591316    //if (opt & 0x4) G.be_quiet++; // -n
    12601317    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));
    12641319    }
    12651320    while (opt_f) { // -f
    12661321        char *line;
    12671322        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) {
    12701325            add_cmd(line);
    12711326            free(line);
    12721327        }
    12731328        fclose(cmdfile);
    1274         opt_f = opt_f->link;
    1275         /* we leak opt_f here... */
    12761329    }
    12771330    /* if we didn't get a pattern from -e or -f, use argv[0] */
    12781331    if (!(opt & 0x18)) {
    1279         if (!argc)
     1332        if (!*argv)
    12801333            bb_show_usage();
    12811334        add_cmd_block(*argv++);
    1282         argc--;
    12831335    }
    12841336    /* Flush any unfinished commands. */
     
    12951347            bb_error_msg_and_die(bb_msg_requires_arg, "-i");
    12961348        add_input_file(stdin);
    1297         process_files();
    12981349    } else {
    12991350        int i;
    13001351        FILE *file;
    13011352
    1302         for (i = 0; i < argc; i++) {
     1353        for (i = 0; argv[i]; i++) {
    13031354            struct stat statbuf;
    13041355            int nonstdoutfd;
     
    13201371
    13211372            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 */
    13291377            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 */
    13301380            fchmod(nonstdoutfd, statbuf.st_mode);
     1381            fchown(nonstdoutfd, statbuf.st_uid, statbuf.st_gid);
    13311382            add_input_file(file);
    13321383            process_files();
     
    13351386            G.nonstdout = stdout;
    13361387            /* unlink(argv[i]); */
    1337             // FIXME: error check / message?
    1338             rename(G.outname, argv[i]);
     1388            xrename(G.outname, argv[i]);
    13391389            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();
    13451399
    13461400    return status;
  • branches/2.2.9/mindi-busybox/editors/vi.c

    r1765 r2725  
    44 * Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.com>
    55 *
    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.
    77 */
    88
     
    2424#include "libbb.h"
    2525
     26/* the CRASHME code is unmaintained, and doesn't currently build */
    2627#define ENABLE_FEATURE_VI_CRASHME 0
    2728
     29
    2830#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)
    3035#else
     36# define Isprint(c) isprint_asciionly(c)
     37#endif
     38
     39#else
     40
    3141/* 0x9b is Meta-ESC */
     42#if ENABLE_FEATURE_VI_8BIT
    3243#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
    3450
    3551enum {
    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.
    3757    MAX_SCR_COLS = CONFIG_FEATURE_VI_MAX_LEN,
     58    MAX_SCR_ROWS = CONFIG_FEATURE_VI_MAX_LEN,
    3859};
    39 
    40 // Misc. non-Ascii keys that report an escape sequence
    41 #define VI_K_UP         (char)128   // cursor key Up
    42 #define VI_K_DOWN       (char)129   // cursor key Down
    43 #define VI_K_RIGHT      (char)130   // Cursor Key Right
    44 #define VI_K_LEFT       (char)131   // cursor key Left
    45 #define VI_K_HOME       (char)132   // Cursor Key Home
    46 #define VI_K_END        (char)133   // Cursor Key End
    47 #define VI_K_INSERT     (char)134   // Cursor Key Insert
    48 #define VI_K_PAGEUP     (char)135   // Cursor Key Page Up
    49 #define VI_K_PAGEDOWN       (char)136   // Cursor Key Page Down
    50 #define VI_K_FUN1       (char)137   // Function Key F1
    51 #define VI_K_FUN2       (char)138   // Function Key F2
    52 #define VI_K_FUN3       (char)139   // Function Key F3
    53 #define VI_K_FUN4       (char)140   // Function Key F4
    54 #define VI_K_FUN5       (char)141   // Function Key F5
    55 #define VI_K_FUN6       (char)142   // Function Key F6
    56 #define VI_K_FUN7       (char)143   // Function Key F7
    57 #define VI_K_FUN8       (char)144   // Function Key F8
    58 #define VI_K_FUN9       (char)145   // Function Key F9
    59 #define VI_K_FUN10      (char)146   // Function Key F10
    60 #define VI_K_FUN11      (char)147   // Function Key F11
    61 #define VI_K_FUN12      (char)148   // Function Key F12
    6260
    6361/* vt102 typical ESC sequence */
    6462/* 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"
    6765/* terminal bell sequence */
    68 static const char bell[] ALIGN1 = "\007";
     66#define bell "\007"
    6967/* 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"
    7270/* Cursor motion arbitrary destination ESC sequence */
    73 static const char CMrc[] ALIGN1 = "\033[%d;%dH";
     71#define CMrc "\033[%u;%uH"
    7472/* 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
     80static const char modifying_cmds[] = "cCdDJoOpPrRxX<>~";
     81#endif
    7882
    7983enum {
     
    9296};
    9397
     98
    9499/* vi.c expects chars to be unsigned. */
    95100/* busybox build system provides that, but it's better */
    96101/* to audit and fix the source */
    97102
    98 static smallint vi_setops;
     103struct 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;
    99111#define VI_AUTOINDENT 1
    100112#define VI_SHOWMATCH  2
     
    107119#define err_method (vi_setops & VI_ERR_METHOD)
    108120
    109 
    110 static smallint editing;        // >0 while we are editing a file
    111                                 // [code audit says "can be 0 or 1 only"]
    112 static smallint cmd_mode;       // 0=command  1=insert 2=replace
    113 static smallint file_modified;  // buffer contents changed
    114 static smallint last_file_modified = -1;
    115 static int fn_start;            // index of first cmd line file name
    116 static int save_argc;           // how many file names on cmd line
    117 static int cmdcnt;              // repetition count
    118 static int rows, columns;       // the terminal screen is this size
    119 static int crow, ccol, offset;  // cursor is on Crow x Ccol with Horz Ofset
    120 static char *status_buffer;     // mesages to the user
    121 #define STATUS_BUFFER_LEN  200
    122 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 line
    125 static char *current_filename;               // current file name
    126 //static char *text, *end;        // pointers to the user data in memory
    127 static char *screen;            // pointer to the virtual screen buffer
    128 static int screensize;          //            and its size
    129 static char *screenbegin;       // index into text[], of top line on the screen
    130 //static char *dot;               // where all the action takes place
    131 static int tabstop;
    132 static char erase_char;         // the users erase character
    133 static char last_input_char;    // last char read from user
    134 static char last_forward_char;  // last char searched for with 'f'
    135 
    136121#if ENABLE_FEATURE_VI_READONLY
    137 //static smallint vi_readonly, readonly;
    138 static smallint readonly_mode = 0;
     122    smallint readonly_mode;
    139123#define SET_READONLY_FILE(flags)        ((flags) |= 0x01)
    140124#define SET_READONLY_MODE(flags)        ((flags) |= 0x02)
    141125#define UNSET_READONLY_FILE(flags)      ((flags) &= 0xfe)
    142126#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
    148157
    149158#if ENABLE_FEATURE_VI_DOT_CMD
    150 static smallint adding2q;       // are we currently adding user input to q
    151 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"
    153162#endif
    154163#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
    155 static int last_row;        // where the cursor was last moved to
     164    int last_row;        // where the cursor was last moved to
    156165#endif
    157166#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;
    162168#endif
    163169#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 */
    173174#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"
    174183    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"
    176184    char *mark[28];         // user marks points somewhere in text[]-  a-z and previous context ''
    177185    char *context_start, *context_end;
    178186#endif
    179     /* a few references only */
    180187#if ENABLE_FEATURE_VI_USE_SIGNALS
    181     jmp_buf restart;        // catch_sig()
    182 #endif
    183     struct termios term_orig, term_vi;  // remember what the cooked mode was
     188    sigjmp_buf restart;     // catch_sig()
     189#endif
     190    struct termios term_orig, term_vi; // remember what the cooked mode was
    184191#if ENABLE_FEATURE_VI_COLON
    185192    char *initial_cmds[3];  // currently 2 entries, NULL terminated
    186193#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];
    187209};
    188210#define G (*ptr_to_globals)
     
    192214#define dot            (G.dot           )
    193215#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
    194258#define YDreg          (G.YDreg         )
    195259#define Ureg           (G.Ureg          )
     
    201265#define term_vi        (G.term_vi       )
    202266#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
    203279
    204280static int init_text_buffer(char *); // init from file or create new
    205281static void edit_file(char *);  // edit one file
    206 static void do_cmd(char);   // execute a command
     282static void do_cmd(int);    // execute a command
    207283static int next_tabstop(int);
    208284static void sync_cursor(char *, int *, int *);  // synchronize the screen cursor to dot
     
    227303static char *new_screen(int, int);  // malloc virtual screen memory
    228304static 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[]!
     307static uintptr_t stupid_insert(char *, char);   // stupidly insert the char c at 'p'
     308static int find_range(char **, char **, char);  // return pointers for an object
    231309static int st_test(char *, int, int, char *);   // helper for skip_thing()
    232310static char *skip_thing(char *, int, int, int); // skip some object
    233311static char *find_pair(char *, char);   // find matching pair ()  []  {}
    234312static 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[]!
     315static uintptr_t text_hole_make(char *, int);   // at "p", make a 'size' byte hole
    236316static char *yank_delete(char *, char *, int, int); // yank text[] into register then delete
    237317static void show_help(void);    // display some help info
    238318static void rawmode(void);  // set "raw" mode on tty
    239319static 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)
     321static int mysleep(int);
     322static int readit(void);    // read (maybe cursor) key from stdin
     323static int get_one_char(void);  // read 1 char from stdin
    243324static 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[]!
    245329static int file_insert(const char *, char *, int);
    246 #else
    247 static int file_insert(const char *, char *);
    248 #endif
    249330static 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
    250334static void place_cursor(int, int, int);
    251335static void screen_erase(void);
    252336static void clear_to_eol(void);
    253337static void clear_to_eos(void);
     338static void go_bottom_and_clear_to_eol(void);
    254339static void standout_start(void);   // send "start reverse video" sequence
    255340static void standout_end(void); // send "end reverse video" sequence
    256341static void flash(int);     // flash the terminal screen
    257342static void show_status_line(void); // put a message on the bottom line
    258 static void psb(const char *, ...);     // Print Status Buf
    259 static void psbs(const char *, ...);    // Print Status Buf in standout mode
    260 static void ni(const char *);       // display messages
     343static void status_line(const char *, ...);     // print to status buf
     344static void status_line_bold(const char *, ...);
     345static void not_implemented(const char *); // display "Not implemented" message
    261346static int format_edit_status(void);    // format file status on status line
    262347static void redraw(int);    // force a full screen refresh
    263 static void format_line(char*, char*, int);
     348static char* format_line(char* /*, int*/);
    264349static void refresh(int);   // update the terminal from screen[]
    265350
     
    292377#endif
    293378#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[]!
     381static uintptr_t string_insert(char *, const char *);   // insert the string at 'p'
    295382#endif
    296383#if ENABLE_FEATURE_VI_YANKMARK
     
    311398}
    312399
    313 int vi_main(int argc, char **argv);
     400int vi_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
    314401int vi_main(int argc, char **argv)
    315402{
    316403    int c;
    317     RESERVE_CONFIG_BUFFER(STATUS_BUFFER, STATUS_BUFFER_LEN);
     404
     405    INIT_G();
    318406
    319407#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
    320408    my_pid = getpid();
    321409#endif
    322 
    323     PTR_TO_GLOBALS = xzalloc(sizeof(G));
    324 
    325410#if ENABLE_FEATURE_VI_CRASHME
    326411    srand((long) my_pid);
    327412#endif
    328 
    329     status_buffer = STATUS_BUFFER;
    330     last_status_cksum = 0;
    331     text = NULL;
    332 
    333413#ifdef NO_SUCH_APPLET_YET
    334414    /* If we aren't "vi", we are "view" */
     
    338418#endif
    339419
    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;
    348421    //  1-  process $HOME/.exrc file (not inplemented yet)
    349422    //  2-  process EXINIT variable from environment
     
    353426        char *p = getenv("EXINIT");
    354427        if (p && *p)
    355             initial_cmds[0] = xstrdup(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) {
    359432        switch (c) {
    360433#if ENABLE_FEATURE_VI_CRASHME
     
    368441            break;
    369442#endif
    370             //case 'r': // recover flag-  ignore- we don't use tmp file
    371             //case 'x': // encryption flag- ignore
    372             //case 'c': // execute command first
    373443#if ENABLE_FEATURE_VI_COLON
    374444        case 'c':       // cmd line vi command
    375445            if (*optarg)
    376                 initial_cmds[initial_cmds[0] != 0] = xstrdup(optarg);
     446                initial_cmds[initial_cmds[0] != 0] = xstrndup(optarg, MAX_INPUT_LEN);
    377447            break;
    378             //case 'h': // help -- just use default
    379 #endif
     448#endif
     449        case 'H':
     450            show_help();
     451            /* fall through */
    380452        default:
    381             show_help();
     453            bb_show_usage();
    382454            return 1;
    383455        }
     
    390462
    391463    //----- 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;
    398468    }
    399469    //-----------------------------------------------------------
     
    411481    /* allocate/reallocate text buffer */
    412482    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;
    416484    screenbegin = dot = end = text = xzalloc(text_size);
    417485
     
    425493        rc = 0;
    426494    } else {
    427         rc = file_insert(fn, text
    428             USE_FEATURE_VI_READONLY(, 1));
     495        rc = file_insert(fn, text, 1);
    429496    }
    430497    file_modified = 0;
     
    437504}
    438505
    439 static void edit_file(char * fn)
    440 {
    441     char c;
     506#if ENABLE_FEATURE_VI_WIN_RESIZE
     507static 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
     520static 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;
    442526    int size;
    443 
    444527#if ENABLE_FEATURE_VI_USE_SIGNALS
    445528    int sig;
    446529#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
    452532    rawmode();
    453533    rows = 24;
    454534    columns = 80;
    455535    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
    458554    new_screen(rows, columns);  // get memory for virtual screen
    459555    init_text_buffer(fn);
     
    470566
    471567#if ENABLE_FEATURE_VI_USE_SIGNALS
    472     catch_sig(0);
     568    signal(SIGINT, catch_sig);
    473569    signal(SIGWINCH, winch_sig);
    474570    signal(SIGTSTP, suspend_sig);
    475     sig = setjmp(restart);
     571    sig = sigsetjmp(restart, 1);
    476572    if (sig != 0) {
    477573        screenbegin = dot = text;
     
    485581    c = '\0';
    486582#if ENABLE_FEATURE_VI_DOT_CMD
    487     free(last_modifying_cmd);
    488583    free(ioq_start);
    489     ioq = ioq_start = last_modifying_cmd = NULL;
     584    ioq = ioq_start = NULL;
     585    lmc_len = 0;
    490586    adding2q = 0;
    491587#endif
    492     redraw(FALSE);          // dont force every col re-draw
    493588
    494589#if ENABLE_FEATURE_VI_COLON
     
    497592        int n = 0;
    498593
    499         while ((p = initial_cmds[n])) {
     594        while ((p = initial_cmds[n]) != NULL) {
    500595            do {
    501596                q = p;
    502                 p = strchr(q,'\n');
     597                p = strchr(q, '\n');
    503598                if (p)
    504599                    while (*p == '\n')
     
    513608    }
    514609#endif
     610    redraw(FALSE);          // dont force every col re-draw
    515611    //------This is the main Vi cmd handling loop -----------------------
    516612    while (editing > 0) {
     
    521617            } else {
    522618                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;
    524621                refresh(FALSE);
    525622            }
     
    537634        // These are commands that change text[].
    538635        // 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)
    540640         && strchr(modifying_cmds, c)
    541641        ) {
     
    544644#endif
    545645        do_cmd(c);      // execute the user command
    546         //
     646
    547647        // poll to see if there is input already waiting. if we are
    548648        // not able to display output fast enough to keep up, skip
    549649        // the display update until we catch up with input.
    550         if (mysleep(0) == 0) {
    551             // no input pending- so update output
     650        if (!readbuffer[0] && mysleep(0) == 0) {
     651            // no input pending - so update output
    552652            refresh(FALSE);
    553653            show_status_line();
     
    560660    //-------------------------------------------------------------------
    561661
    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();
    564663    cookmode();
     664#undef cur_line
    565665}
    566666
    567667//----- The Colon commands -------------------------------------
    568668#if ENABLE_FEATURE_VI_COLON
    569 static char *get_one_address(char * p, int *addr)   // get colon addr, if present
     669static char *get_one_address(char *p, int *addr)    // get colon addr, if present
    570670{
    571671    int st;
    572672    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;)
    580675
    581676    *addr = -1;         // assume no addr
     
    584679        q = begin_line(dot);
    585680        *addr = count_lines(text, q);
     681    }
    586682#if ENABLE_FEATURE_VI_YANKMARK
    587     } else if (*p == '\'') {    // is this a mark addr
     683    else if (*p == '\'') {  // is this a mark addr
    588684        p++;
    589685        c = tolower(*p);
     
    594690            q = mark[(unsigned char) c];
    595691            if (q != NULL) {    // is mark valid
    596                 *addr = count_lines(text, q);   // count lines
     692                *addr = count_lines(text, q);
    597693            }
    598694        }
     695    }
    599696#endif
    600697#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;
    610702        if (*p == '/')
    611703            p++;
     
    615707        }
    616708        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
    619712        p++;
    620713        q = begin_line(end - 1);
     
    623716        sscanf(p, "%d%n", addr, &st);
    624717        p += st;
    625     } else {            // I don't reconise this
    626         // unrecognised address- assume -1
     718    } else {
     719        // unrecognized address - assume -1
    627720        *addr = -1;
    628721    }
     
    665758    int l = strlen(opname) - 1; /* opname have + ' ' */
    666759
     760    // maybe strncmp? we had tons of erroneous strncasecmp's...
    667761    if (strncasecmp(a, opname, l) == 0
    668762     || strncasecmp(a, short_opname, 2) == 0
     
    676770#endif
    677771
    678 static void colon(char * buf)
     772// buf must be no longer than MAX_INPUT_LEN!
     773static void colon(char *buf)
    679774{
    680775    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];
    682777    int i, l, li, ch, b, e;
    683     int useforce = FALSE, forced = FALSE;
     778    int useforce, forced = FALSE;
    684779
    685780    // :3154    // if (-e line 3154) goto it  else stay put
     
    699794
    700795    if (!buf[0])
    701         goto vc1;
     796        goto ret;
    702797    if (*buf == ':')
    703798        buf++;          // move past the ':'
     
    708803    r = end - 1;
    709804    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;
    713806
    714807    // look for optional address(es)  :.  :1  :1,9   :'q,'a   :%
     
    725818        *buf1++ = *buf++;
    726819    }
     820    *buf1 = '\0';
    727821    // get any ARGuments
    728822    while (isblank(*buf))
    729823        buf++;
    730824    strcpy(args, buf);
     825    useforce = FALSE;
    731826    buf1 = last_char_is(cmd, '!');
    732827    if (buf1) {
     
    759854    }
    760855#if ENABLE_FEATURE_ALLOW_EXEC
    761     else if (strncmp(cmd, "!", 1) == 0) {   // run a cmd
     856    else if (cmd[0] == '!') {   // run a cmd
    762857        int retcode;
    763858        // :!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();
    767860        cookmode();
    768861        retcode = system(orig_buf + 1); // run the cmd
     
    771864        rawmode();
    772865        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
    777869        if (b < 0) {    // no addr given- use defaults
    778870            b = e = count_lines(text, dot);
    779871        }
    780         psb("%d", b);
    781     } else if (strncasecmp(cmd, "delete", i) == 0) {    // delete lines
     872        status_line("%d", b);
     873    } else if (strncmp(cmd, "delete", i) == 0) {    // delete lines
    782874        if (b < 0) {    // no addr given- use defaults
    783875            q = begin_line(dot);    // assume .,. for the range
     
    786878        dot = yank_delete(q, r, 1, YANKDEL);    // save, then delete lines
    787879        dot_skip_over_ws();
    788     } else if (strncasecmp(cmd, "edit", i) == 0) {  // Edit a file
     880    } else if (strncmp(cmd, "edit", i) == 0) {  // Edit a file
    789881        // don't edit, if the current file has been modified
    790         if (file_modified && ! useforce) {
    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;
    793885        }
    794886        if (args[0]) {
     
    800892        } else {
    801893            // 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;
    804896        }
    805897
    806898        if (init_text_buffer(fn) < 0)
    807             goto vc1;
     899            goto ret;
    808900
    809901#if ENABLE_FEATURE_VI_YANKMARK
     
    819911        // how many lines in text[]?
    820912        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")
    823915            " %dL, %dC", current_filename,
    824916            (file_size(fn) < 0 ? " [New file]" : ""),
    825             USE_FEATURE_VI_READONLY(
     917            IF_FEATURE_VI_READONLY(
    826918                ((readonly_mode) ? " [Readonly]" : ""),
    827919            )
    828920            li, ch);
    829     } else if (strncasecmp(cmd, "file", i) == 0) {  // what File is this
     921    } else if (strncmp(cmd, "file", i) == 0) {  // what File is this
    830922        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;
    833925        }
    834926        if (args[0]) {
     
    840932            last_status_cksum = 0;  // force status update
    841933        }
    842     } else if (strncasecmp(cmd, "features", i) == 0) {  // what features are available
     934    } else if (strncmp(cmd, "features", i) == 0) {  // what features are available
    843935        // 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();
    846937        cookmode();
    847938        show_help();
    848939        rawmode();
    849940        Hit_Return();
    850     } else if (strncasecmp(cmd, "list", i) == 0) {  // literal print line
     941    } else if (strncmp(cmd, "list", i) == 0) {  // literal print line
    851942        if (b < 0) {    // no addr given- use defaults
    852943            q = begin_line(dot);    // assume .,. for the range
    853944            r = end_line(dot);
    854945        }
    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();
    857947        puts("\r");
    858948        for (; q <= r; q++) {
     
    864954                c = '.';
    865955                standout_start();
    866                 }
     956            }
    867957            if (c == '\n') {
    868958                write1("$\r");
    869959            } else if (c < ' ' || c == 127) {
    870                 putchar('^');
     960                bb_putchar('^');
    871961                if (c == 127)
    872962                    c = '?';
     
    874964                    c += '@';
    875965            }
    876             putchar(c);
     966            bb_putchar(c);
    877967            if (c_is_no_print)
    878968                standout_end();
    879969        }
    880 #if ENABLE_FEATURE_VI_SET
    881  vc2:
    882 #endif
    883970        Hit_Return();
    884     } else if (strncasecmp(cmd, "quit", i) == 0 // Quit
    885             || strncasecmp(cmd, "next", i) == 0 // edit next file
     971    } else if (strncmp(cmd, "quit", i) == 0 // quit
     972            || strncmp(cmd, "next", i) == 0 // edit next file
    886973    ) {
     974        int n;
    887975        if (useforce) {
    888976            // force end of argv list
     
    891979            }
    892980            editing = 0;
    893             goto vc1;
     981            goto ret;
    894982        }
    895983        // don't exit if the file been modified
    896984        if (file_modified) {
    897             psbs("No write since last change (:%s! overrides)",
     985            status_line_bold("No write since last change (:%s! overrides)",
    898986                 (*cmd == 'q' ? "quit" : "next"));
    899             goto vc1;
     987            goto ret;
    900988        }
    901989        // 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;
    909998        }
    910999        editing = 0;
    911     } else if (strncasecmp(cmd, "read", i) == 0) {  // read file into text[]
     1000    } else if (strncmp(cmd, "read", i) == 0) {  // read file into text[]
    9121001        fn = args;
    9131002        if (!fn[0]) {
    914             psbs("No filename given");
    915             goto vc1;
     1003            status_line_bold("No filename given");
     1004            goto ret;
    9161005        }
    9171006        if (b < 0) {    // no addr given- use defaults
     
    9211010        if (b != 0)
    9221011            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        }
    9241017        if (ch < 0)
    925             goto vc1;   // nothing was inserted
     1018            goto ret;   // nothing was inserted
    9261019        // how many lines in text[]?
    9271020        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")
    9301023            " %dL, %dC", fn,
    931             USE_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
     1024            IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
    9321025            li, ch);
    9331026        if (ch > 0) {
     
    9351028            if (q <= dot)
    9361029                dot += ch;
    937             file_modified++;
    938         }
    939     } else if (strncasecmp(cmd, "rewind", i) == 0) {    // rewind cmd line args
    940         if (file_modified && ! useforce) {
    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)");
    9421035        } else {
    9431036            // reset the filenames to edit
     
    9461039        }
    9471040#if ENABLE_FEATURE_VI_SET
    948     } else if (strncasecmp(cmd, "set", i) == 0) {   // set or clear features
     1041    } else if (strncmp(cmd, "set", i) == 0) {   // set or clear features
    9491042#if ENABLE_FEATURE_VI_SETOPTS
    9501043        char *argp;
     
    9541047        if (!args[0] || strcasecmp(args, "all") == 0) {
    9551048            // print out values of all options
    956             place_cursor(rows - 1, 0, FALSE);   // go to Status line, bottom of screen
    957             clear_to_eol(); // clear the line
    958             printf("----------------------------------------\r\n");
    9591049#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;
    9761064        }
    9771065#if ENABLE_FEATURE_VI_SETOPTS
    9781066        argp = args;
    9791067        while (*argp) {
    980             if (strncasecmp(argp, "no", 2) == 0)
     1068            if (strncmp(argp, "no", 2) == 0)
    9811069                i = 2;      // ":set noautoindent"
    9821070            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);
    9841072            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;
    9911079            }
    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);
    9961082        }
    9971083#endif /* FEATURE_VI_SETOPTS */
    9981084#endif /* FEATURE_VI_SET */
    9991085#if ENABLE_FEATURE_VI_SEARCH
    1000     } else if (strncasecmp(cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
     1086    } else if (cmd[0] == 's') { // substitute a pattern with a replacement pattern
    10011087        char *ls, *F, *R;
    10021088        int gflag;
     
    10091095        F = orig_buf + 2;   // start of "find"
    10101096        R = strchr(F, c);   // middle delimiter
    1011         if (!R) goto colon_s_fail;
     1097        if (!R)
     1098            goto colon_s_fail;
    10121099        *R++ = '\0';    // terminate "find"
    10131100        buf1 = strchr(R, c);
    1014         if (!buf1) goto colon_s_fail;
     1101        if (!buf1)
     1102            goto colon_s_fail;
    10151103        *buf1++ = '\0'; // terminate "replace"
    10161104        if (*buf1 == 'g') { // :s/foo/bar/g
     
    10301118            buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
    10311119            if (buf1) {
     1120                uintptr_t bias;
    10321121                // we found the "find" pattern - delete it
    10331122                text_hole_delete(buf1, buf1 + strlen(F) - 1);
    10341123                // 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 */
    10361128                // check for "global"  :s/foo/bar/g
    10371129                if (gflag == 1) {
     
    10451137        }
    10461138#endif /* FEATURE_VI_SEARCH */
    1047     } else if (strncasecmp(cmd, "version", i) == 0) {  // show software version
    1048         psb("%s", BB_VER " " BB_BT);
    1049     } else if (strncasecmp(cmd, "write", i) == 0  // write text to file
    1050             || strncasecmp(cmd, "wq", i) == 0
    1051             || strncasecmp(cmd, "wn", i) == 0
    1052             || strncasecmp(cmd, "x", i) == 0
     1139    } 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])
    10531145    ) {
    10541146        // is there a file name to write to?
     
    10581150#if ENABLE_FEATURE_VI_READONLY
    10591151        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;
    10621154        }
    10631155#endif
     
    10811173        if (l < 0) {
    10821174            if (l == -1)
    1083                 psbs("\"%s\" %s", fn, strerror(errno));
     1175                status_line_bold("\"%s\" %s", fn, strerror(errno));
    10841176        } else {
    1085             psb("\"%s\" %dL, %dC", fn, li, l);
     1177            status_line("\"%s\" %dL, %dC", fn, li, l);
    10861178            if (q == text && r == end - 1 && l == ch) {
    10871179                file_modified = 0;
    10881180                last_file_modified = -1;
    10891181            }
    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            ) {
    10931187                editing = 0;
    10941188            }
    10951189        }
    1096 #if ENABLE_FEATURE_VI_READONLY
    1097  vc3:;
    1098 #endif
    10991190#if ENABLE_FEATURE_VI_YANKMARK
    1100     } else if (strncasecmp(cmd, "yank", i) == 0) {  // yank lines
     1191    } else if (strncmp(cmd, "yank", i) == 0) {  // yank lines
    11011192        if (b < 0) {    // no addr given- use defaults
    11021193            q = begin_line(dot);    // assume .,. for the range
     
    11051196        text_yank(q, r, YDreg);
    11061197        li = count_lines(q, r);
    1107         psb("Yank %d lines (%d chars) into [%c]",
     1198        status_line("Yank %d lines (%d chars) into [%c]",
    11081199                li, strlen(reg[YDreg]), what_reg());
    11091200#endif
    11101201    } else {
    11111202        // cmd unknown
    1112         ni(cmd);
    1113     }
    1114  vc1:
     1203        not_implemented(cmd);
     1204    }
     1205 ret:
    11151206    dot = bound_dot(dot);   // make sure "dot" is valid
    11161207    return;
    11171208#if ENABLE_FEATURE_VI_SEARCH
    11181209 colon_s_fail:
    1119     psb(":s expression missing delimiters");
     1210    status_line(":s expression missing delimiters");
    11201211#endif
    11211212}
     
    11251216static void Hit_Return(void)
    11261217{
    1127     char c;
    1128 
    1129     standout_start();   // start reverse video
     1218    int c;
     1219
     1220    standout_start();
    11301221    write1("[Hit return to continue]");
    1131     standout_end();     // end reverse video
    1132     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;
    11341225    redraw(TRUE);       // force redraw all
    11351226}
     
    11411232
    11421233//----- Synchronize the cursor to Dot --------------------------
    1143 static void sync_cursor(char * d, int *row, int *col)
     1234static NOINLINE void sync_cursor(char *d, int *row, int *col)
    11441235{
    11451236    char *beg_cur;  // begin and end of "d" line
    1146     char *end_scr;  // begin and end of screen
    11471237    char *tp;
    11481238    int cnt, ro, co;
     
    11501240    beg_cur = begin_line(d);    // first char of cur line
    11511241
    1152     end_scr = end_screen(); // last char of screen
    1153 
    11541242    if (beg_cur < screenbegin) {
    1155         // "d" is before  top line on screen
     1243        // "d" is before top line on screen
    11561244        // how many lines do we have to move
    11571245        cnt = count_lines(beg_cur, screenbegin);
     
    11641252            }
    11651253        }
    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            }
    11781270        }
    11791271    }
     
    11881280    // find out what col "d" is on
    11891281    co = 0;
    1190     do {                // drive "co" to correct column
    1191         if (*tp == '\n' || *tp == '\0')
     1282    while (tp < d) { // drive "co" to correct column
     1283        if (*tp == '\n') //vda || *tp == '\0')
    11921284            break;
    11931285        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) {
    11951288                break;
    1196             } else {
    1197                 co = next_tabstop(co);
    11981289            }
    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    }
    12031297
    12041298    // "co" is the column where "dot" is.
     
    12331327
    12341328//----- 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
     1329static 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    }
    12391337    return p;
    12401338}
    12411339
    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
     1340static 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    }
    12461347    return p;
    12471348}
    12481349
    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
     1350static char *dollar_line(char *p) // return pointer to just before NL line
     1351{
     1352    p = end_line(p);
    12531353    // Try to stay off of the Newline
    12541354    if (*p == '\n' && (p - begin_line(p)) > 0)
     
    12571357}
    12581358
    1259 static char *prev_line(char * p) // return pointer first char prev line
     1359static char *prev_line(char *p) // return pointer first char prev line
    12601360{
    12611361    p = begin_line(p);  // goto begining of cur line
    1262     if (p[-1] == '\n' && p > text)
     1362    if (p > text && p[-1] == '\n')
    12631363        p--;            // step to prev line
    12641364    p = begin_line(p);  // goto begining of prev line
     
    12661366}
    12671367
    1268 static char *next_line(char * p) // return pointer first char next line
     1368static char *next_line(char *p) // return pointer first char next line
    12691369{
    12701370    p = end_line(p);
    1271     if (*p == '\n' && p < end - 1)
     1371    if (p < end - 1 && *p == '\n')
    12721372        p++;            // step to next line
    12731373    return p;
     
    12881388}
    12891389
    1290 static int count_lines(char * start, char * stop) // count line from start to stop
     1390// count line from start to stop
     1391static int count_lines(char *start, char *stop)
    12911392{
    12921393    char *q;
    12931394    int cnt;
    12941395
    1295     if (stop < start) { // start and stop are backwards- reverse them
     1396    if (stop < start) { // start and stop are backwards- reverse them
    12961397        q = start;
    12971398        start = stop;
     
    12991400    }
    13001401    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')
    13041406            cnt++;
     1407        start++;
    13051408    }
    13061409    return cnt;
     
    13401443}
    13411444
    1342 static char *move_to_col(char * p, int l)
     1445static char *move_to_col(char *p, int l)
    13431446{
    13441447    int co;
     
    13461449    p = begin_line(p);
    13471450    co = 0;
    1348     do {
    1349         if (*p == '\n' || *p == '\0')
     1451    while (co < l && p < end) {
     1452        if (*p == '\n') //vda || *p == '\0')
    13501453            break;
    13511454        if (*p == '\t') {
    13521455            co = next_tabstop(co);
    13531456        } 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    }
    13571462    return p;
    13581463}
     
    13751480        if (dir < 0) {
    13761481            // scroll Backwards
    1377             // ctrl-Y  scroll up one line
     1482            // ctrl-Y scroll up one line
    13781483            screenbegin = prev_line(screenbegin);
    13791484        } else {
    13801485            // scroll Forwards
    1381             // ctrl-E  scroll down one line
     1486            // ctrl-E scroll down one line
    13821487            screenbegin = next_line(screenbegin);
    13831488        }
     
    14041509}
    14051510
    1406 static char *bound_dot(char * p) // make sure  text[0] <= P < "end"
     1511static char *bound_dot(char *p) // make sure  text[0] <= P < "end"
    14071512{
    14081513    if (p >= end && end > text) {
     
    14471552
    14481553#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);
     1554static int mycmp(const char *s1, const char *s2, int len)
     1555{
    14541556    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);
    14581560}
    14591561
    14601562// search for pattern starting at p
    1461 static char *char_search(char * p, const char * pat, int dir, int range)
     1563static char *char_search(char *p, const char *pat, int dir, int range)
    14621564{
    14631565#ifndef REGEX_SEARCH
     
    15171619    if (q != 0) {
    15181620        // 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);
    15201622        i = 0;          // return p if pattern not compiled
    15211623        goto cs1;
     
    15521654#endif /* FEATURE_VI_SEARCH */
    15531655
    1554 static char *char_insert(char * p, char c) // insert the char c at 'p'
     1656static char *char_insert(char *p, char c) // insert the char c at 'p'
    15551657{
    15561658    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
    15591660        refresh(FALSE); // show the ^
    15601661        c = get_one_char();
    15611662        *p = c;
    15621663        p++;
    1563         file_modified++;    // has the file been modified
     1664        file_modified++;
    15641665    } else if (c == 27) {   // Is this an ESC?
    15651666        cmd_mode = 0;
     
    15831684            c = '\n';   // translate \r to \n
    15841685        sp = p;         // remember addr of insert
    1585         p = stupid_insert(p, c);    // insert the char
     1686        p += 1 + stupid_insert(p, c);   // insert the char
    15861687#if ENABLE_FEATURE_VI_SETOPTS
    15871688        if (showmatch && strchr(")]}", *sp) != NULL) {
     
    15901691        if (autoindent && c == '\n') {  // auto indent the new line
    15911692            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;
    15961703            }
    15971704        }
     
    16011708}
    16021709
    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[]!
     1712static 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
     1722static int find_range(char **start, char **stop, char c)
     1723{
     1724    char *save_dot, *p, *q, *t;
     1725    int cnt, multiline = 0;
    16181726
    16191727    save_dot = dot;
     
    16271735        }
    16281736        q = end_line(q);
    1629     } else if (strchr("^%$0bBeEft", c)) {
     1737    } else if (strchr("^%$0bBeEfth\b\177", c)) {
    16301738        // These cmds operate on char positions
    16311739        do_cmd(c);      // execute movement cmd
     
    16561764        q = dot;
    16571765    } 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
    16611789    *start = p;
    16621790    *stop = q;
    1663     if (q < p) {
    1664         *start = q;
    1665         *stop = p;
    1666     }
    16671791    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
     1795static int st_test(char *p, int type, int dir, char *tested)
    16721796{
    16731797    char c, c0, ci;
     
    16811805    if (type == S_BEFORE_WS) {
    16821806        c = ci;
    1683         test = ((!isspace(c)) || c == '\n');
     1807        test = (!isspace(c) || c == '\n');
    16841808    }
    16851809    if (type == S_TO_WS) {
    16861810        c = c0;
    1687         test = ((!isspace(c)) || c == '\n');
     1811        test = (!isspace(c) || c == '\n');
    16881812    }
    16891813    if (type == S_OVER_WS) {
    16901814        c = c0;
    1691         test = ((isspace(c)));
     1815        test = isspace(c);
    16921816    }
    16931817    if (type == S_END_PUNCT) {
    16941818        c = ci;
    1695         test = ((ispunct(c)));
     1819        test = ispunct(c);
    16961820    }
    16971821    if (type == S_END_ALNUM) {
    16981822        c = ci;
    1699         test = ((isalnum(c)) || c == '_');
     1823        test = (isalnum(c) || c == '_');
    17001824    }
    17011825    *tested = c;
     
    17031827}
    17041828
    1705 static char *skip_thing(char * p, int linecnt, int dir, int type)
     1829static char *skip_thing(char *p, int linecnt, int dir, int type)
    17061830{
    17071831    char c;
     
    17211845
    17221846// find matching char of pair  ()  []  {}
    1723 static char *find_pair(char * p, char c)
     1847static char *find_pair(char *p, const char c)
    17241848{
    17251849    char match, *q;
     
    17301854    dir = 1;            // assume forward
    17311855    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;
    17531862    }
    17541863    for (q = p + dir; text <= q && q < end; q += dir) {
     
    17681877#if ENABLE_FEATURE_VI_SETOPTS
    17691878// show the matching char of a pair,  ()  []  {}
    1770 static void showmatching(char * p)
     1879static void showmatching(char *p)
    17711880{
    17721881    char *q, *save_dot;
     
    17881897#endif /* FEATURE_VI_SETOPTS */
    17891898
    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[]!
     1902static uintptr_t text_hole_make(char *p, int size)  // at "p", make a 'size' byte hole
     1903{
     1904    uintptr_t bias = 0;
    17951905
    17961906    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);
    18071921    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;
    18121924}
    18131925
    18141926//  close a hole in text[]
    1815 static char *text_hole_delete(char * p, char * q) // delete "p" thru "q", inclusive
     1927static char *text_hole_delete(char *p, char *q) // delete "p" through "q", inclusive
    18161928{
    18171929    char *src, *dest;
     
    18341946    if (src >= end)
    18351947        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);
    18391949 thd_atend:
    18401950    end = end - hole_size;  // adjust the new END
     
    18431953    if (end <= text)
    18441954        dest = end = text;  // keep pointers valid
    1845     file_modified++;    // has the file been modified
     1955    file_modified++;
    18461956 thd0:
    18471957    return dest;
     
    18511961// if dist <= 0, do not include, or go past, a NewLine
    18521962//
    1853 static char *yank_delete(char * start, char * stop, int dist, int yf)
     1963static char *yank_delete(char *start, char *stop, int dist, int yf)
    18541964{
    18551965    char *p;
     
    18952005#endif
    18962006#if ENABLE_FEATURE_VI_YANKMARK
    1897     "\n\tLine marking with  'x"
    1898     "\n\tNamed buffers with  \"x"
     2007    "\n\tLine marking with 'x"
     2008    "\n\tNamed buffers with \"x"
    18992009#endif
    19002010#if ENABLE_FEATURE_VI_READONLY
     
    19182028}
    19192029
    1920 static inline void print_literal(char * buf, const char * s) // copy s to buf, convert unprintable
    1921 {
    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             else
    1943                 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 
    19542030#if ENABLE_FEATURE_VI_DOT_CMD
    19552031static void start_new_cmd_q(char c)
    19562032{
    1957     // release old cmd
    1958     free(last_modifying_cmd);
    19592033    // get buffer for new cmd
    1960     last_modifying_cmd = xzalloc(MAX_LINELEN);
    19612034    // 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 queue
     2035    if (cmdcnt > 0) {
     2036        lmc_len = sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
     2037    } else { // just save char c onto queue
    19652038        last_modifying_cmd[0] = c;
     2039        lmc_len = 1;
     2040    }
    19662041    adding2q = 1;
    19672042}
     
    19792054 || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
    19802055 || 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[]!
     2058static uintptr_t string_insert(char *p, const char *s) // insert the string at 'p'
     2059{
     2060    uintptr_t bias;
     2061    int i;
    19842062
    19852063    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;
    19882070        for (cnt = 0; *s != '\0'; s++) {
    19892071            if (*s == '\n')
    19902072                cnt++;
    19912073        }
     2074        status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
     2075    }
     2076#endif
     2077    return bias;
     2078}
     2079#endif
     2080
    19922081#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;
     2082static 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);
    20182091    return p;
    20192092}
     
    20502123}
    20512124
    2052 static inline char *swap_context(char * p) // goto new context for '' command make this the current context
     2125static char *swap_context(char *p) // goto new context for '' command make this the current context
    20532126{
    20542127    char *tmp;
     
    20802153    term_vi.c_cc[VTIME] = 0;
    20812154    erase_char = term_vi.c_cc[VERASE];
    2082     tcsetattr(0, TCSANOW, &term_vi);
     2155    tcsetattr_stdin_TCSANOW(&term_vi);
    20832156}
    20842157
    20852158static void cookmode(void)
    20862159{
    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
    20912165//----- 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 {
     2166static void winch_sig(int sig UNUSED_PARAM)
     2167{
     2168    int save_errno = errno;
     2169    // FIXME: do it in main loop!!!
    20952170    signal(SIGWINCH, winch_sig);
    2096     if (ENABLE_FEATURE_VI_WIN_RESIZE)
    2097         get_terminal_width_height(0, &columns, &rows);
     2171    query_screen_dimensions();
    20982172    new_screen(rows, columns);  // get memory for virtual screen
    20992173    redraw(TRUE);       // re-draw the screen
     2174    errno = save_errno;
    21002175}
    21012176
    21022177//----- 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
     2178static 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
    21082184
    21092185    signal(SIGTSTP, suspend_sig);
    21102186    signal(SIGCONT, SIG_DFL);
    2111     kill(my_pid, SIGCONT);
     2187    //kill(my_pid, SIGCONT); // huh? why? we are already "continued"...
     2188    errno = save_errno;
    21122189}
    21132190
    21142191//----- 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 screen
    2118     clear_to_eol();     // Erase to end of line
    2119     cookmode();         // terminal to "cooked"
     2192static 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"
    21202197
    21212198    signal(SIGCONT, cont_sig);
    21222199    signal(SIGTSTP, SIG_DFL);
    21232200    kill(my_pid, SIGTSTP);
     2201    errno = save_errno;
    21242202}
    21252203
     
    21282206{
    21292207    signal(SIGINT, catch_sig);
    2130     if (sig)
    2131         longjmp(restart, sig);
     2208    siglongjmp(restart, sig);
    21322209}
    21332210#endif /* FEATURE_VI_USE_SIGNALS */
    21342211
    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;
     2212static 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}
    21532220
    21542221//----- 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
     2222static 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    }
    22722233    return c;
    22732234}
    22742235
    22752236//----- IO Routines --------------------------------------------
    2276 static char get_one_char(void)
    2277 {
    2278     static char c;
     2237static int get_one_char(void)
     2238{
     2239    int c;
    22792240
    22802241#if ENABLE_FEATURE_VI_DOT_CMD
    2281     // ! adding2q  && ioq == 0  read()
    2282     // ! adding2q  && ioq != 0  *ioq
    2283     // adding2q         *last_modifying_cmd= read()
    22842242    if (!adding2q) {
    22852243        // we are not adding to the q.
     
    22902248        } else {
    22912249            // there is a queue to get chars from first
    2292             c = *ioq++;
     2250            // careful with correct sign expansion!
     2251            c = (unsigned char)*ioq++;
    22932252            if (c == '\0') {
    22942253                // the end of the q, read from STDIN
     
    23012260        // adding STDIN chars to q
    23022261        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;
    23112267        }
    23122268    }
     
    23142270    c = readit();       // get the users input
    23152271#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)
     2276static char *get_input_line(const char *prompt)
     2277{
     2278    // char [MAX_INPUT_LEN]
     2279#define buf get_input_line__buf
     2280
     2281    int c;
    23252282    int i;
    23262283
    23272284    strcpy(buf, prompt);
    23282285    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();
    23312287    write1(prompt);      // write out the :, /, or ? prompt
    23322288
    23332289    i = strlen(buf);
    2334     while (i < MAX_LINELEN) {
    2335         c = get_one_char(); // read user input
     2290    while (i < MAX_INPUT_LEN) {
     2291        c = get_one_char();
    23362292        if (c == '\n' || c == '\r' || c == 27)
    2337             break;      // is this end of input
     2293            break;      // this is end of input
    23382294        if (c == erase_char || c == 8 || c == 127) {
    23392295            // 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
    23452299                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);
    23522305        }
    23532306    }
    23542307    refresh(FALSE);
    2355     free(obufp);
    2356     obufp = xstrdup(buf);
    2357     return obufp;
     2308    return buf;
     2309#undef buf
    23582310}
    23592311
     
    23642316
    23652317    cnt = -1;
    2366     if (fn && fn[0] && stat(fn, &st_buf) == 0)  // see if file exists
     2318    if (fn && stat(fn, &st_buf) == 0)   // see if file exists
    23672319        cnt = (int) st_buf.st_size;
    23682320    return cnt;
    23692321}
    23702322
    2371 static int file_insert(const char * fn, char *p
    2372         USE_FEATURE_VI_READONLY(, int update_ro_status))
     2323// might reallocate text[]!
     2324static int file_insert(const char *fn, char *p, int update_ro_status)
    23732325{
    23742326    int cnt = -1;
     
    23782330    /* Validate file */
    23792331    if (stat(fn, &statbuf) < 0) {
    2380         psbs("\"%s\" %s", fn, strerror(errno));
     2332        status_line_bold("\"%s\" %s", fn, strerror(errno));
    23812333        goto fi0;
    23822334    }
    2383     if ((statbuf.st_mode & S_IFREG) == 0) {
     2335    if (!S_ISREG(statbuf.st_mode)) {
    23842336        // This is not a regular file
    2385         psbs("\"%s\" Not a regular file", fn);
     2337        status_line_bold("\"%s\" Not a regular file", fn);
    23862338        goto fi0;
    23872339    }
    2388     /* // this check is done by open()
    2389     if ((statbuf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
    2390         // dont have any read permissions
    2391         psbs("\"%s\" Not readable", fn);
    2392         goto fi0;
    2393     }
    2394     */
    23952340    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");
    23972342        goto fi0;
    23982343    }
     
    24012346    fd = open(fn, O_RDONLY);
    24022347    if (fd < 0) {
    2403         psbs("\"%s\" %s", fn, strerror(errno));
     2348        status_line_bold("\"%s\" %s", fn, strerror(errno));
    24042349        goto fi0;
    24052350    }
    24062351    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);
    24112354    if (cnt < 0) {
    2412         psbs("\"%s\" %s", fn, strerror(errno));
     2355        status_line_bold("\"%s\" %s", fn, strerror(errno));
    24132356        p = text_hole_delete(p, p + size - 1);  // un-do buffer insert
    24142357    } else if (cnt < size) {
    24152358        // There was a partial read, shrink unused space text[]
    24162359        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);
    24182361    }
    24192362    if (cnt >= size)
     
    24352378}
    24362379
    2437 
    2438 static int file_write(char * fn, char * first, char * last)
     2380static int file_write(char *fn, char *first, char *last)
    24392381{
    24402382    int fd, cnt, charcnt;
    24412383
    24422384    if (fn == 0) {
    2443         psbs("No current filename");
     2385        status_line_bold("No current filename");
    24442386        return -2;
    24452387    }
    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);
    24492393    if (fd < 0)
    24502394        return -1;
    24512395    cnt = last - first + 1;
    2452     charcnt = write(fd, first, cnt);
     2396    charcnt = full_write(fd, first, cnt);
     2397    ftruncate(fd, charcnt);
    24532398    if (charcnt == cnt) {
    24542399        // good write
    2455         //file_modified = FALSE; // the file has not been modified
     2400        //file_modified = FALSE;
    24562401    } else {
    24572402        charcnt = 0;
     
    24702415//  .       ...     .
    24712416//  22,0    ...     22,79
    2472 //  23,0    ...     23,79   status line
    2473 //
     2417//  23,0    ...     23,79   <- status line
    24742418
    24752419//----- 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];
     2420static 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
    24792431    char *cm;
    2480 #if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
    2481     char cm2[MAX_LINELEN];
    2482     char *screenp;
    2483     // char cm3[MAX_LINELEN];
    2484     int Rrow = last_row;
    2485 #endif
    2486 
    2487     memset(cm1, '\0', MAX_LINELEN);  // clear the buffer
    24882432
    24892433    if (row < 0) row = 0;
     
    24952439    sprintf(cm1, CMrc, row + 1, col + 1);
    24962440    cm = cm1;
    2497     if (!opti)
    2498         goto pc0;
    24992441
    25002442#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;
    25332480#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
    2534  pc0:
    2535     write1(cm);                 // move the cursor
     2481    write1(cm);
    25362482}
    25372483
     
    25402486{
    25412487    write1(Ceol);   // Erase from cursor to end of line
     2488}
     2489
     2490static 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
    25422494}
    25432495
     
    26122564    }
    26132565    if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
    2614         last_status_cksum= cksum;       // remember if we have seen this line
    2615         place_cursor(rows - 1, 0, FALSE);   // put cursor on status line
     2566        last_status_cksum = cksum;      // remember if we have seen this line
     2567        go_bottom_and_clear_to_eol();
    26162568        write1(status_buffer);
    2617         clear_to_eol();
    26182569        if (have_status_msg) {
    26192570            if (((int)strlen(status_buffer) - (have_status_msg - 1)) >
     
    26262577        place_cursor(crow, ccol, FALSE);    // put cursor back in correct place
    26272578    }
    2628     fflush(stdout);
     2579    fflush_all();
    26292580}
    26302581
    26312582//----- format the status buffer, the bottom line of screen ------
    26322583// format status buffer, with STANDOUT mode
    2633 static void psbs(const char *format, ...)
     2584static void status_line_bold(const char *format, ...)
    26342585{
    26352586    va_list args;
     
    26372588    va_start(args, format);
    26382589    strcpy(status_buffer, SOs); // Terminal standout mode on
    2639     vsprintf(status_buffer + strlen(status_buffer), format, args);
     2590    vsprintf(status_buffer + sizeof(SOs)-1, format, args);
    26402591    strcat(status_buffer, SOn); // Terminal standout mode off
    26412592    va_end(args);
     
    26452596
    26462597// format status buffer
    2647 static void psb(const char *format, ...)
     2598static void status_line(const char *format, ...)
    26482599{
    26492600    va_list args;
     
    26562607}
    26572608
    2658 static void ni(const char * s) // display messages
    2659 {
    2660     char buf[MAX_LINELEN];
     2609// copy s to buf, convert unprintable
     2610static 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
     2651static void not_implemented(const char *s)
     2652{
     2653    char buf[MAX_INPUT_LEN];
    26612654
    26622655    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 line
    2667 {
    2668     static int tot;
     2656    status_line_bold("\'%s\' is not implemented", buf);
     2657}
     2658
     2659// show file status on status line
     2660static int format_edit_status(void)
     2661{
    26692662    static const char cmd_mode_indicator[] ALIGN1 = "-IR-";
     2663
     2664#define tot format_edit_status__tot
     2665
    26702666    int cur, percent, ret, trunc_at;
    26712667
     
    27182714
    27192715    return trunc_at;  /* had to truncate */
     2716#undef tot
    27202717}
    27212718
     
    27242721{
    27252722    place_cursor(0, 0, FALSE);  // put cursor in correct place
    2726     clear_to_eos();     // tel terminal to erase display
     2723    clear_to_eos();     // tell terminal to erase display
    27272724    screen_erase();     // erase the internal screen buffer
    27282725    last_status_cksum = 0;  // force status update
     
    27322729
    27332730//----- Format a text[] line into a buffer ---------------------
    2734 static void format_line(char *dest, char *src, int li)
    2735 {
     2731static char* format_line(char *src /*, int li*/)
     2732{
     2733    unsigned char c;
    27362734    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) {
    27462743            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'
    27592762                }
    2760             } else {
    2761                 dest[co++] = '^';
    2762                 if (c == 0x7f)
    2763                     c = '?';
    2764                 else
    2765                     c += '@';       // make it visible
    27662763            }
    27672764        }
    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        }
    27712773        if (src >= end)
    27722774            break;
    27732775    }
     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;
    27742786}
    27752787
     
    27812793static void refresh(int full_screen)
    27822794{
    2783     static int old_offset;
     2795#define old_offset refresh__old_offset
    27842796
    27852797    int li, changed;
    2786     char buf[MAX_SCR_COLS];
    27872798    char *tp, *sp;      // pointer into text[] and screen[]
    2788 #if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
    2789     int last_li = -2; // last line that changed- for optimizing cursor movement
    2790 #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    }
    27942805    sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
    27952806    tp = screenbegin;   // index into text[] of top line
     
    27982809    for (li = 0; li < rows - 1; li++) {
    27992810        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*/);
    28042814
    28052815        // 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
    28092823        changed = FALSE;    // assume no change
    2810         cs= 0;
    2811         ce= columns-1;
     2824        cs = 0;
     2825        ce = columns - 1;
    28122826        sp = &screen[li * columns]; // start of screen line
    28132827        if (full_screen) {
     
    28182832        // look forward for first difference between buf and screen
    28192833        for (; cs <= ce; cs++) {
    2820             if (buf[cs + offset] != sp[cs]) {
     2834            if (out_buf[cs] != sp[cs]) {
    28212835                changed = TRUE; // mark for redraw
    28222836                break;
     
    28242838        }
    28252839
    2826         // look backward for last difference between buf and screen
    2827         for ( ; ce >= cs; ce--) {
    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]) {
    28292843                changed = TRUE; // mark for redraw
    28302844                break;
     
    28402854
    28412855        // 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 buf
     2856        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
    28462860        if (changed) {
    2847             //  copy changed part of buffer to virtual screen
    2848             memmove(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);
    28492863
    28502864            // 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            //}
    28662872
    28672873            // 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
    28922882}
    28932883
     
    29142904
    29152905//----- 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
     2906static 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);
    29262919
    29272920    show_status_line();
     
    29292922    /* if this is a cursor key, skip these checks */
    29302923    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:
    29392933            goto key_cmd_mode;
    29402934    }
     
    29422936    if (cmd_mode == 2) {
    29432937        //  flip-flop Insert/Replace mode
    2944         if (c == VI_K_INSERT)
     2938        if (c == KEYCODE_INSERT)
    29452939            goto dc_i;
    29462940        // we are 'R'eplacing the current *dot with new char
     
    29592953    if (cmd_mode == 1) {
    29602954        //  hitting "Insert" twice means "R" replace mode
    2961         if (c == VI_K_INSERT) goto dc5;
     2955        if (c == KEYCODE_INSERT) goto dc5;
    29622956        // insert the char c at "dot"
    29632957        if (1 <= c || Isprint(c)) {
     
    29952989        //case ')': // )-
    29962990        //case '*': // *-
    2997         //case ',': // ,-
    29982991        //case '=': // =-
    29992992        //case '@': // @-
     
    30093002        //case '_': // _-
    30103003        //case '`': // `-
    3011         //case 'g': // g-
    30123004        //case 'u': // u- FIXME- there is no undo
    30133005        //case 'v': // v-
    3014     default:            // unrecognised command
     3006    default:            // unrecognized command
    30153007        buf[0] = c;
    30163008        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);
    30233010        end_cmd_q();    // stop adding to q
    30243011    case 0x00:          // nul- ignore
    30253012        break;
    30263013    case 2:         // ctrl-B  scroll up   full screen
    3027     case VI_K_PAGEUP:   // Cursor Key Page Up
     3014    case KEYCODE_PAGEUP:    // Cursor Key Page Up
    30283015        dot_scroll(rows - 2, -1);
    30293016        break;
    3030 #if ENABLE_FEATURE_VI_USE_SIGNALS
    3031     case 0x03:          // ctrl-C   interrupt
    3032         longjmp(restart, 1);
    3033         break;
    3034     case 26:            // ctrl-Z suspend
    3035         suspend_sig(SIGTSTP);
    3036         break;
    3037 #endif
    30383017    case 4:         // ctrl-D  scroll down half screen
    30393018        dot_scroll((rows - 2) / 2, 1);
     
    30433022        break;
    30443023    case 6:         // ctrl-F  scroll down full screen
    3045     case VI_K_PAGEDOWN: // Cursor Key Page Down
     3024    case KEYCODE_PAGEDOWN:  // Cursor Key Page Down
    30463025        dot_scroll(rows - 2, 1);
    30473026        break;
     
    30503029        break;
    30513030    case 'h':           // h- move left
    3052     case VI_K_LEFT: // cursor key Left
     3031    case KEYCODE_LEFT:  // cursor key Left
    30533032    case 8:     // ctrl-H- move left    (This may be ERASE char)
    30543033    case 0x7f:  // DEL- move left   (This may be ERASE char)
    3055         if (cmdcnt-- > 1) {
     3034        if (--cmdcnt > 0) {
    30563035            do_cmd(c);
    3057         }               // repeat cnt
     3036        }
    30583037        dot_left();
    30593038        break;
    30603039    case 10:            // Newline ^J
    30613040    case 'j':           // j- goto next line, same col
    3062     case VI_K_DOWN: // cursor key Down
    3063         if (cmdcnt-- > 1) {
     3041    case KEYCODE_DOWN:  // cursor key Down
     3042        if (--cmdcnt > 0) {
    30643043            do_cmd(c);
    3065         }               // repeat cnt
     3044        }
    30663045        dot_next();     // go to next B-o-l
    30673046        dot = move_to_col(dot, ccol + offset);  // try stay in same col
     
    30783057    case 13:            // Carriage Return ^M
    30793058    case '+':           // +- goto next line
    3080         if (cmdcnt-- > 1) {
     3059        if (--cmdcnt > 0) {
    30813060            do_cmd(c);
    3082         }               // repeat cnt
     3061        }
    30833062        dot_next();
    30843063        dot_skip_over_ws();
     
    30993078    case ' ':           // move right
    31003079    case 'l':           // move right
    3101     case VI_K_RIGHT:    // Cursor Key Right
    3102         if (cmdcnt-- > 1) {
     3080    case KEYCODE_RIGHT: // Cursor Key Right
     3081        if (--cmdcnt > 0) {
    31033082            do_cmd(c);
    3104         }               // repeat cnt
     3083        }
    31053084        dot_right();
    31063085        break;
    31073086#if ENABLE_FEATURE_VI_YANKMARK
    31083087    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;
    31133091        } else {
    31143092            indicate_error(c);
     
    31163094        break;
    31173095    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?
    31223098            // get the b-o-l
    3123             q = mark[(unsigned char) c1];
     3099            q = mark[c1];
    31243100            if (text <= q && q < end) {
    31253101                dot = q;
     
    31403116        // correct location! It could be off by many lines!
    31413117        // 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?
    31463120            // remember the line
    3147             mark[(int) c1] = dot;
     3121            mark[c1] = dot;
    31483122        } else {
    31493123            indicate_error(c);
     
    31533127    case 'p':           // p- put register after
    31543128        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());
    31573131            break;
    31583132        }
     
    31743148                dot_right();    // move to right, can move to NL
    31753149        }
    3176         dot = string_insert(dot, p);    // insert the string
     3150        string_insert(dot, p);  // insert the string
    31773151        end_cmd_q();    // stop adding to q
    31783152        break;
     
    31823156            q = end_line(dot);
    31833157            p = text_hole_delete(p, q); // delete cur line
    3184             p = string_insert(p, reg[Ureg]);    // insert orig line
     3158            p += string_insert(p, reg[Ureg]);   // insert orig line
    31853159            dot = p;
    31863160            dot_skip_over_ws();
     
    31893163#endif /* FEATURE_VI_YANKMARK */
    31903164    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();
    31933168            do_cmd(c);
    3194         }               // repeat cnt
     3169        }
    31953170        dot = end_line(dot);
    31963171        break;
     
    32163191        // dont separate these two commands. 'f' depends on ';'
    32173192        //
    3218         //**** fall thru to ... ';'
     3193        //**** fall through to ... ';'
    32193194    case ';':           // ;- look at rest of line for last forward char
    3220         if (cmdcnt-- > 1) {
     3195        if (--cmdcnt > 0) {
    32213196            do_cmd(';');
    3222         }               // repeat cnt
     3197        }
    32233198        if (last_forward_char == 0)
    32243199            break;
     
    32303205            dot = q;
    32313206        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
    32323221    case '-':           // -- goto prev line
    3233         if (cmdcnt-- > 1) {
     3222        if (--cmdcnt > 0) {
    32343223            do_cmd(c);
    3235         }               // repeat cnt
     3224        }
    32363225        dot_prev();
    32373226        dot_skip_over_ws();
     
    32413230        // Stuff the last_modifying_cmd back into stdin
    32423231        // and let it be re-executed.
    3243         if (last_modifying_cmd != 0) {
     3232        if (lmc_len > 0) {
     3233            last_modifying_cmd[lmc_len] = 0;
    32443234            ioq = ioq_start = xstrdup(last_modifying_cmd);
    32453235        }
     
    32523242        buf[1] = '\0';
    32533243        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;
    32553247            goto dc3; // if no pat re-use old pat
     3248        }
    32563249        if (q[0]) {       // strlen(q) > 1: new pat- save it and find
    32573250            // there is a new pat
     
    32633256        break;
    32643257    case 'N':           // N- backward search for last pattern
    3265         if (cmdcnt-- > 1) {
     3258        if (--cmdcnt > 0) {
    32663259            do_cmd(c);
    3267         }               // repeat cnt
     3260        }
    32683261        dir = BACK;     // assume BACKWARD search
    32693262        p = dot - 1;
     
    32773270        // search rest of text[] starting at next char
    32783271        // if search fails return orignal "p" not the "p+1" address
    3279         if (cmdcnt-- > 1) {
     3272        if (--cmdcnt > 0) {
    32803273            do_cmd(c);
    3281         }               // repeat cnt
     3274        }
    32823275 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;
    32913278        if (last_search_pattern[0] == '?') {
    32923279            dir = BACK;
     
    33173304 dc2:
    33183305        if (*msg)
    3319             psbs("%s", msg);
     3306            status_line_bold("%s", msg);
    33203307        break;
    33213308    case '{':           // {- move backward paragraph
     
    33583345        if (cnt <= 0)
    33593346            break;
    3360         if (strncasecmp(p, "quit", cnt) == 0
    3361          || strncasecmp(p, "q!", cnt) == 0   // delete lines
     3347        if (strncmp(p, "quit", cnt) == 0
     3348         || strncmp(p, "q!", cnt) == 0   // delete lines
    33623349        ) {
    33633350            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)");
    33653352            } else {
    33663353                editing = 0;
    33673354            }
    3368         } else if (strncasecmp(p, "write", cnt) == 0
    3369                 || strncasecmp(p, "wq", cnt) == 0
    3370                 || strncasecmp(p, "wn", cnt) == 0
    3371                 || strncasecmp(p, "x", cnt) == 0
     3355        } 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])
    33723359        ) {
    33733360            cnt = file_write(current_filename, text, end - 1);
    33743361            if (cnt < 0) {
    33753362                if (cnt == -1)
    3376                     psbs("Write error: %s", strerror(errno));
     3363                    status_line_bold("Write error: %s", strerror(errno));
    33773364            } else {
    33783365                file_modified = 0;
    33793366                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);
    33813368                if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'
    33823369                 || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'
     
    33853372                }
    33863373            }
    3387         } else if (strncasecmp(p, "file", cnt) == 0) {
     3374        } else if (strncmp(p, "file", cnt) == 0) {
    33883375            last_status_cksum = 0;  // force status update
    33893376        } else if (sscanf(p, "%d", &j) > 0) {
    33903377            dot = find_line(j);     // go to line # j
    33913378            dot_skip_over_ws();
    3392         } else {        // unrecognised cmd
    3393             ni(p);
     3379        } else {        // unrecognized cmd
     3380            not_implemented(p);
    33943381        }
    33953382#endif /* !FEATURE_VI_COLON */
     
    34273414    case 'A':           // A- append at e-o-l
    34283415        dot_end();      // go to e-o-l
    3429         //**** fall thru to ... 'a'
     3416        //**** fall through to ... 'a'
    34303417    case 'a':           // a- append after current char
    34313418        if (*dot != '\n')
     
    34363423    case 'E':           // E- end of a blank-delimited word
    34373424    case 'W':           // W- forward a blank-delimited word
    3438         if (cmdcnt-- > 1) {
     3425        if (--cmdcnt > 0) {
    34393426            do_cmd(c);
    3440         }               // repeat cnt
     3427        }
    34413428        dir = FORWARD;
    34423429        if (c == 'B')
     
    34623449#endif
    34633450        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 */
    34643463    case 'G':       // G- goto to a line number (default= E-O-F)
    34653464        dot = end - 1;              // assume E-O-F
     
    34743473            cmdcnt = (rows - 1);
    34753474        }
    3476         if (cmdcnt-- > 1) {
     3475        if (--cmdcnt > 0) {
    34773476            do_cmd('+');
    3478         }               // repeat cnt
     3477        }
    34793478        dot_skip_over_ws();
    34803479        break;
     
    34823481        dot_begin();    // 0
    34833482        dot_skip_over_ws();
    3484         //**** fall thru to ... 'i'
     3483        //**** fall through to ... 'i'
    34853484    case 'i':           // i- insert before current char
    3486     case VI_K_INSERT:   // Cursor Key Insert
     3485    case KEYCODE_INSERT:    // Cursor Key Insert
    34873486 dc_i:
    34883487        cmd_mode = 1;   // start insrting
    34893488        break;
    34903489    case 'J':           // J- join current and next lines together
    3491         if (cmdcnt-- > 2) {
     3490        if (--cmdcnt > 1) {
    34923491            do_cmd(c);
    3493         }               // repeat cnt
     3492        }
    34943493        dot_end();      // move to NL
    34953494        if (dot < end - 1) {    // make sure not last char in text[]
     
    35073506            cmdcnt = (rows - 1);
    35083507        }
    3509         if (cmdcnt-- > 1) {
     3508        if (--cmdcnt > 0) {
    35103509            do_cmd('-');
    3511         }               // repeat cnt
     3510        }
    35123511        dot_begin();
    35133512        dot_skip_over_ws();
     
    35373536        cmd_mode = 2;
    35383537        break;
     3538    case KEYCODE_DELETE:
     3539        c = 'x';
     3540        // fall through
    35393541    case 'X':           // X- delete char before dot
    35403542    case 'x':           // x- delete the current char
    35413543    case 's':           // s- substitute the current char
    3542         if (cmdcnt-- > 1) {
     3544        if (--cmdcnt > 0) {
    35433545            do_cmd(c);
    3544         }               // repeat cnt
     3546        }
    35453547        dir = 0;
    35463548        if (c == 'X')
     
    35643566        if (file_modified) {
    35653567            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);
    35673569                break;
    35683570            }
     
    35703572            if (cnt < 0) {
    35713573                if (cnt == -1)
    3572                     psbs("Write error: %s", strerror(errno));
     3574                    status_line_bold("Write error: %s", strerror(errno));
    35733575            } else if (cnt == (end - 1 - text + 1)) {
    35743576                editing = 0;
     
    35843586    case 'b':           // b- back a word
    35853587    case 'e':           // e- end of word
    3586         if (cmdcnt-- > 1) {
     3588        if (--cmdcnt > 0) {
    35873589            do_cmd(c);
    3588         }               // repeat cnt
     3590        }
    35893591        dir = FORWARD;
    35903592        if (c == 'b')
     
    36083610    case 'Y':           // Y- Yank a line
    36093611#endif
     3612    {
     3613        int yf, ml, whole = 0;
    36103614        yf = YANKDEL;   // assume either "c" or "d"
    36113615#if ENABLE_FEATURE_VI_YANKMARK
     
    36163620        if (c != 'Y')
    36173621            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);
    36193624        if (c1 == 27) { // ESC- user changed mind and wants out
    36203625            c = c1 = 27;    // Escape- do nothing
     
    36283633                }
    36293634            }
    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) {
    36373650            if (c == 'c') {
    36383651                dot = char_insert(dot, '\n');
    36393652                // on the last line of file don't move to prev line
    3640                 if (dot != (end-1)) {
     3653                if (whole && dot != (end-1)) {
    36413654                    dot_prev();
    36423655                }
     
    36453658                dot_skip_over_ws();
    36463659            }
    3647         } else {
    3648             // could not recognize object
    3649             c = c1 = 27;    // error-
    3650             indicate_error(c);
    36513660        }
    36523661        if (c1 != 27) {
     
    36693678                    cnt++;
    36703679            }
    3671             psb("%s %d lines (%d chars) using [%c]",
     3680            status_line("%s %d lines (%d chars) using [%c]",
    36723681                buf, cnt, strlen(reg[YDreg]), what_reg());
    36733682#endif
     
    36753684        }
    36763685        break;
     3686    }
    36773687    case 'k':           // k- goto prev line, same col
    3678     case VI_K_UP:       // cursor key Up
    3679         if (cmdcnt-- > 1) {
     3688    case KEYCODE_UP:        // cursor key Up
     3689        if (--cmdcnt > 0) {
    36803690            do_cmd(c);
    3681         }               // repeat cnt
     3691        }
    36823692        dot_prev();
    36833693        dot = move_to_col(dot, ccol + offset);  // try stay in same col
     
    36873697        if (*dot != '\n') {
    36883698            *dot = c1;
    3689             file_modified++;    // has the file been modified
     3699            file_modified++;
    36903700        }
    36913701        end_cmd_q();    // stop adding to q
     
    36963706        if (*dot == last_forward_char)
    36973707            dot_left();
    3698         last_forward_char= 0;
     3708        last_forward_char = 0;
    36993709        break;
    37003710    case 'w':           // w- forward a word
    3701         if (cmdcnt-- > 1) {
     3711        if (--cmdcnt > 0) {
    37023712            do_cmd(c);
    3703         }               // repeat cnt
     3713        }
    37043714        if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
    37053715            dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
     
    37273737        break;
    37283738    case '~':           // ~- flip the case of letters   a-z -> A-Z
    3729         if (cmdcnt-- > 1) {
     3739        if (--cmdcnt > 0) {
    37303740            do_cmd(c);
    3731         }               // repeat cnt
     3741        }
    37323742        if (islower(*dot)) {
    37333743            *dot = toupper(*dot);
    3734             file_modified++;    // has the file been modified
     3744            file_modified++;
    37353745        } else if (isupper(*dot)) {
    37363746            *dot = tolower(*dot);
    3737             file_modified++;    // has the file been modified
     3747            file_modified++;
    37383748        }
    37393749        dot_right();
     
    37413751        break;
    37423752        //----- The Cursor and Function Keys -----------------------------
    3743     case VI_K_HOME: // Cursor Key Home
     3753    case KEYCODE_HOME:  // Cursor Key Home
    37443754        dot_begin();
    37453755        break;
    37463756        // 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
    37603772    }
    37613773
     
    37823794}
    37833795
     3796/* NB!  the CRASHME code is unmaintained, and doesn't currently build */
    37843797#if ENABLE_FEATURE_VI_CRASHME
    37853798static int totalcmds = 0;
     
    37913804static int Pp = 99;             // Put command Probability
    37923805static 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] = {
     3806static const char chars[20] = "\t012345 abcdABCD-=.$";
     3807static const char *const words[20] = {
    37953808    "this", "is", "a", "test",
    37963809    "broadcast", "the", "emergency", "of",
     
    37993812    "back", "January", "Febuary", "March"
    38003813};
    3801 const char *const lines[20] = {
     3814static const char *const lines[20] = {
    38023815    "You should have received a copy of the GNU General Public License\n",
    38033816    "char c, cm, *cmd, *cmd1;\n",
     
    38213834    "This is too much english for a computer geek.\n",
    38223835};
    3823 char *multilines[20] = {
     3836static char *multilines[20] = {
    38243837    "You should have received a copy of the GNU General Public License\n",
    38253838    "char c, cm, *cmd, *cmd1;\n",
     
    38553868
    38563869    // is there already a command running?
    3857     if (readed_for_parse > 0)
     3870    if (readbuffer[0] > 0)
    38583871        goto cd1;
    38593872 cd0:
    3860     startrbi = rbi = 0;
     3873    readbuffer[0] = 'X';
     3874    startrbi = rbi = 1;
    38613875    sleeptime = 0;          // how long to pause between commands
    3862     memset(readbuffer, '\0', MAX_LINELEN);   // clear the read buffer
     3876    memset(readbuffer, '\0', sizeof(readbuffer));
    38633877    // generate a command by percentages
    38643878    percent = (int) lrand48() % 100;        // get a number from 0-99
     
    39323946        strcat(readbuffer, "\033");
    39333947    }
    3934     readed_for_parse = strlen(readbuffer);
     3948    readbuffer[0] = strlen(readbuffer + 1);
    39353949 cd1:
    39363950    totalcmds++;
     
    39453959
    39463960    time_t tim;
    3947     char d[2], msg[MAX_LINELEN];
     3961    char d[2], msg[80];
    39483962
    39493963    msg[0] = '\0';
     
    39683982
    39693983    if (msg[0]) {
    3970         alarm(0);
    39713984        printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
    39723985            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) {
    39753988            if (d[0] == '\n' || d[0] == '\r')
    39763989                break;
    39773990        }
    3978         alarm(3);
    3979     }
    3980     tim = (time_t) time((time_t *) 0);
     3991    }
     3992    tim = time(NULL);
    39813993    if (tim >= (oldtim + 3)) {
    39823994        sprintf(status_buffer,
Note: See TracChangeset for help on using the changeset viewer.