"Fossies" - the Fresh Open Source Software Archive

Member "sfk-1.9.6/sfk.cpp" (22 Feb 2020, 2207413 Bytes) of package /linux/misc/sfk-1.9.6.tar.gz:


The requested HTML page contains a <FORM> tag that is unusable on "Fossies" in "automatic" (rendered) mode so that page is shown as HTML source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "sfk.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.9.5_vs_1.9.6.

    1 /*
    2    The Swiss File Knife Command Line Multi Function Tool
    3    =====================================================
    4    StahlWorks Technologies, http://stahlworks.com/
    5    Provided under the BSD license.
    6 
    7    The whole source code was created with Depeche View Professional,
    8    the world's fastest source code browser and editor.
    9 
   10    1.9.6
   11    Revision 2:
   12    -  rel: 22.02.2020, Minor Update
   13    -  sum: Fixed CRC checksum calculation with 64-bit sfk.
   14            Better handling of multiple network interfaces with sfk ip.
   15    -  FIX: sfk 64 bit: wrong crc checksum calculations.
   16            sfk zip created wrong crc checksums.
   17            sfk unzip showed wrong crc error messages.
   18            sfk crc, crcgento, crccheck produced
   19            wrong results.
   20    -  CHG: OUTPUT CHANGE: sfk ip under windows:
   21            now gives a list of all possible ip's
   22            if more than one network interface exists.
   23            the preferred ip can now be filtered by
   24            environment variable SFK_OWN_NET.
   25            for batch files the output of 'sfk ip'
   26            can also be predefined by SFK_OWN_IP.
   27    -  add: calc: now tolerates whitespace text.
   28    -  add: udpdump: sfk for windows: when listening 
   29            for multicast traffic sfk now listens
   30            on all network interfaces.
   31    -  doc: xed: reasons for unexpected line breaks.
   32    -  fix: compile: vc14 (vs2015) support.
   33    Initial Release:
   34    -  rel: 08.02.2020, Major Update
   35    -  sum: important bugfixes for file selection.
   36            improved help for general options.
   37            improved web server and scripting.
   38    -  FIX: multi directory file selection using
   39             -dir ... -subdir ... -dir ... [-file]
   40            selected far too few files, or stopped
   41            with an error if another -file was given.
   42            check your scripts for this combination.
   43    -  FIX: crash on some -dir -subdir usages in two
   44            subsequent chain commands.
   45    -  chg: httpserv: improved page layout with
   46            more compact file list output.
   47            added option -wide for old format.
   48    -  add: script: system variables
   49            sys.ownscript.name
   50            sys.ownscript.text
   51            to get the filename and text data
   52            of the current running script.
   53    -  add: crccheck -sane to skip files which are
   54            newer than the crc list.
   55    -  fix: option -fileset stopped the command chain,
   56            following commands like +filter were ignored.
   57    -  chg: select: can now use a list of filenames
   58            given by -flist.
   59    -  chg: xfind: now accepts filenames produced by
   60            a previous command (just as extract and
   61            xhexfind already did), allowing to run
   62            a nested search.
   63    -  chg: ascii: alignment of first hex codes,
   64            change of headline text.
   65    -  chg: sfk web: download limit default size
   66            increased from 30 to 100 mb.
   67    -  doc: full rework of sfk help options.
   68    -  doc: select, zip: -flist examples.
   69    -  doc: stat: option -flist.
   70    -  doc: xfind: how to run a nested search with
   71            xfind -names ... +xfind.
   72    -  doc: xfind: how to get result text and filename
   73            within the same line.
   74    -  doc: xfind: example how to search a mail inbox.
   75    -  add: support for zip extension .jmod
   76    internal:
   77    -  add: webserv: option -usehta
   78    -  chg: rework of tcping, tohtml.
   79    -  add: tcpsend -force empty data support
   80    -  add: -every=2nd, 3rd etc.
   81    -  add: syncnames
   82    -  add: snapto outer time, size
   83    -  fix: chain.justNamesFilter nptr check
   84    -  add: mput, alias of putall
   85 
   86    1.9.5
   87    Revision 2:
   88    -  rel: 28.08.2019, Major Update
   89    -  sum: improved difflines and reading from std input.
   90    -  add: sfk jsonform, reformat JSON data for easy
   91            reading and searching
   92    -  add: SFKTray V1.1 now supports display of patterns
   93            up, down, left, right, all instead of just
   94            a single slot. for documentation and example
   95            see: sfk status
   96    -  add: SFKTray Full V1.1 now supports up to 3
   97            instances running in parallel,
   98            with a total of up to 27 lights.
   99    -  add: sfk sft(serv) option -notify=h to show an
  100            up or down arrow in SFKTray on file transfer
  101    -  add: tdifflines to use chain text
  102            together with a file name
  103    -  add: difflines option -swap to change
  104            order of input data
  105    -  add: windows: snapto -slow option to run with
  106            a lower process priority.
  107    -  add: windows: fromclip option -tofile x.
  108    -  chg: hexdump: can now use -pure -recsize=n
  109            to dump n input bytes per output record.
  110    -  fix: crash when using -i with empty input
  111    -  fix: mac compile
  112    Initial Release:
  113    -  rel: 04.06.2019, Major Update
  114    -  sum: sfk snapto can now include text from
  115            office files like .docx .xlsx .odt,
  116            allowing fast loading and browsing
  117            of the output file by Depeche View.
  118    -  add: sfk olist, list only office files
  119            within a folder.
  120    -  add: snapto: option -office to include text
  121            from office files like .docx .xlsx .odt
  122    -  add: snapto, ofind: option -justoffice to read only
  123            office files, but not plain text files.
  124    -  chg: no longer loading binaries with commands
  125            like snapto just due to a file mask,
  126            but still by .extension. use option -wlbin
  127            for the old behaviour.
  128    -  add: difflines: option to read from stdin.
  129    -  fix: difflines: example with variables.
  130    -  fix: require: if no version info was found
  131            it produced a non speaking error.
  132            now it assumes version 0.0.0
  133    -  fix: sfk ose: missing office file support.
  134    -  doc: help office: snapto examples.
  135    -  doc: sfk view: dview office notes.
  136 
  137    1.9.4
  138    Revision 3:
  139    -  rel: 21.03.2019, Minor Update
  140    -  sum: improved sfk name and rand. bugfix
  141            for simple expressions using ortext.
  142    -  fix: simple expressions: crash when using
  143            many [ortext] in the same search pattern.
  144    -  chg: name: no longer searches in meta data
  145            by default, e.g. sfk name 1234 no longer
  146            produces unexpected results because
  147            their filesize is 1234.
  148    -  add: name: option -withmeta and -meta
  149            to also search in meta data of index.
  150    -  add: name: -spat support.
  151    -  add: sfk fileserv, alias of sfk sftserv.
  152    -  add: sfk ... +put, alias of +sft ... cput.
  153    -  add: sfk ... +putall, alias of +sft ... mput.
  154    -  fix: rand: first value per process start
  155            contained very few random
  156    -  add: rand: now supports larger values,
  157            hex output, and setting a seed
  158    -  add: treesize: option -gb[yte] to list
  159            all sizes in gigabytes.
  160    -  add: ping: option -time to display reply
  161            time when pinging a single host
  162    -  doc: ping: info on graphical ping output
  163    Revision 2:
  164    -  rel: 16.02.2019, Major Update
  165    -  sum: Improved office file text search
  166            with options to produce UTF-8 output.
  167    -  fix: .xlsx content loading showed wrong
  168            output text in some cases.
  169    -  add: ofind, oload, ofilter: option -utfout 
  170            to keep UTF-8 encoding on output text.
  171    -  add: ofind: option -utfin to allow use of
  172            UTF-8 encoded search terms with -utfout.
  173    -  add: oload: option -raw to show xml content.
  174    -  chg: oload, ofilter: no longer shows 
  175            zip file comment.
  176    Initial Release:
  177    -  rel: 10.02.2019, Major Update
  178    -  sum: SFK can now search and load office
  179            file text contents, in Open Office
  180            format files like .docx .xlsx .ods .odt.
  181            Search functions now search only in
  182            text files by default.
  183    -  CHG: Syntax Change: xfind now only searches
  184            in plain text files, but not in binaries.
  185            use xfindbin to search also binary files.
  186    -  CHG: Syntax Change: find now only searches
  187            in plain text files, but not in binaries.
  188            use findbin to search also binary files.
  189    -  add: ofind, search text in office files like
  190            .docx .xlsx .ods .odt and plain text files.
  191    -  add: ofilter, filter text of an office file.
  192    -  add: oload, load text of an office file
  193            for display or further processing.
  194    -  add: sfk help office, for details about
  195            supported office file extensions.
  196    -  add: sfk alias, mkcd: support for SFK_PATH
  197            environment variable, to write batch files
  198            only into this folder.
  199    -  chg: sfk getdv: now also suggests to save
  200            to SFK_PATH if given.
  201    -  add: echo: option -stream for best output
  202            transfer to setvar or xed.
  203    -  dep: xtext is deprecated (redundant to xfind)
  204    -  dep: ftext is deprecated (redundant to find)
  205    -  fix: zipto: crash on sub folder names
  206            containing accent characters,
  207            especially on select ... +zipto.
  208    -  chg: sfk data: max content per symbol
  209            is now 8 kbytes instead of 1.
  210    -  fix: xed: sfk ... +xed without any patterns
  211            created unwanted color output.
  212    internal:
  213    rev3:
  214    -  add: fromtcp, replytcp
  215    -  del: olist documentation
  216    -  chg: unified execxfind
  217 
  218    1.9.3
  219    Revision 4:
  220    -  rel: 08.12.2018, Minor Update
  221    -  sum: improved scripting support.
  222            the free SFKTray lite can now display
  223            4 status lights.
  224    -  add: SFKTray Lite now supports 4 status slots.
  225    -  add: windows: fromclip +copy/move support.
  226            allows to mark filenames in cmd.exe
  227            then copy/move the marked names.
  228    -  add: sfk sel a.txt b.txt +copy out support.
  229    -  add: sfk filt in.txt -+pat +copy out support.
  230    -  add: addtovar: option -noline to not append
  231            extra linefeeds.
  232    -  add: setvar: support slash patterns by -spat.
  233    -  add: getvar: option -tofile to write
  234            variable content to a file.
  235    -  add: num: support of negative numbers.
  236    -  add: dir -juststat to show just statistics.
  237    -  add: ftpserv: -verbose now shows path info
  238            on invalid path error, -verbose=2 shows
  239            dump of input line.
  240    -  chg: ftp client: now -quiet no longer tells 
  241            "no filenames from previous commands".
  242    -  fix: rmtree: short confirm "rmtree." did not work.
  243    -  fix: dir repeated with +loop showed wrong
  244            number of total bytes.
  245    -  chg: sfk dir now supports -noname.
  246    -  doc: setvar: examples for string editing.
  247    -  doc: xtext: find lines not matching a pattern.
  248    -  doc: filter: file copy examples.
  249    Revision 3:
  250    -  rel: 01.11.2018, Major Update
  251    -  sum: improved scripting support. bugfix for scripts
  252            running in folders with spaces in their name.
  253    -  FIX: sfk batch: script did not work within
  254            folder names containing spaces,
  255            like C:\users\Foo Bar\myscript.bat.
  256            now sfk script "%~f0" with quotes is used.
  257            fixed also related commands like
  258            sfk samp sfk, sfk samp sfkbash.
  259    -  CHG: chaining: if chaining is stopped due to missing
  260            filenames it now shows a notice again by default,
  261            to avoid unexpected chain stops without any infos.
  262            the notice can no longer be suppressed by -quiet,
  263            but as always by -nonote.
  264    -  fix: option -quiet did not work globally.
  265    -  chg: chaining: -verbose no longer tells how the
  266            command chain is stepped. use -tracechain instead.
  267    -  add: general option -keepchain to never stop the
  268            command chain, even if commands that expect
  269            filenames get none.
  270    -  add: label: support for all general options.
  271    -  add: setglobalopt to set global options.
  272    -  add: chaining support with where, index, script, home.
  273    -  add: encode -base64: option -nowrap to keep
  274            output as one long line.
  275    -  fix: getcol: unexpected set of global -upat option.
  276    -  chg: sfk chars: now supports multiple code parameters.
  277    -  add: ffilter: warning on wrong parameter sequence.
  278    -  fix: copy -ltarg under windows showed source name.
  279    -  chg: zip reading: better error message on missing file.
  280    -  doc: tell: example how to print text starting with '+'.
  281    -  doc: wtou: added perline example.
  282    -  doc: if: info about limited nesting.
  283    -  doc: end: was not searchable.
  284    -  doc: status: infos about limited colors.
  285    -  doc: label: more detailed info about global options.
  286    -  doc: help opt: -tracechain.
  287    -  doc: help chain: info on "no files, stopping x".
  288    Revision 2:
  289    -  rel: 03.10.2018, Major Update
  290    -  sum: added easy installation under windows
  291            by a double click on sfk.exe, then key 1. 
  292            this copies sfk.exe to a folder c:\tools 
  293            and creates a shell icon on the desktop.
  294    -  add: sfk base: optional installation when running sfk
  295            by double click from windows explorer.
  296    -  CHG: Data Change: sfk index, name under windows
  297            no longer use files directly within C:\ 
  298            but only in a folder C:\zz-index, to avoid 
  299            user local storage in a VirtualStore folder.
  300            use sfk name -useold to read old index files.
  301            the new behaviour allows one C:\\ index file for
  302            all users on the same windows machine.
  303    -  add: predefined variable: sys.sfkver for sfk version
  304    -  add: predefined variable: sys.numcols for number of
  305            columns available in console
  306    -  fix: getcol, tabcol: caused unexpected double execution
  307            of following chain commands.
  308    -  add: clipsrc: -h help option
  309    -  del: clipxml: does not work, no longer documented.
  310    Initial Release:
  311    -  rel: 18.09.2018, Major Update
  312    -  sum: added sfk addtovar to collect text lines 
  313            in a variable, sfk crc to create crc-32 checksums,
  314            sfk difflines to list different unique lines 
  315            between two text files. improved ftp client
  316            compatibility. many detail improvements
  317            for scripting, like new variable functions.
  318    -  chg: Syntax Change: command chaining no longer allows
  319            data tunneling through non consuming commands.
  320            in a chain like cmd1 +cmd2 +cmd3 +cmd4 +cmd5
  321            it is no longer possible to send data from
  322            cmd1 to cmd3/4/5 if cmd2 uses no chain data
  323            (e.g. if, tell). this tunneling was inconsistent
  324            and caused endless conflicts by unwanted chain data
  325            in cmd3/4/5. use -keepdata or SET SFK_CONFIG=keepdata
  326            for the old behaviour which was inconsistent 
  327            and worked only sometimes.
  328    -  chg: Syntax Change: sfk if: no longer passes through
  329            any chain data, as this behaviour was ambiguous
  330            and caused endless unwanted chain input.
  331            this means combinations like
  332            +xed /a/b/ +if ... +xed /b/c/
  333            are no longer possible. use '+tif' instead,
  334            or global option -keepdata.
  335    -  add: sfk ... +tif for conditional execution
  336            requesting text and stream data explicitely.
  337    -  add: sfk ... +tgoto to jump to a label passing
  338            text and stream data explicitely.
  339    -  chg: Syntax Change: sfk md5: now only prints the
  340            checksum without filename if only a single
  341            filename is given. use option -name 
  342            for the old behaviour.
  343    -  chg: Data Change: sfk getvar -line no longer
  344            appends CR or LF at extracted line.
  345    -  chg: Data Change: web: no longer produces shell
  346            return code 9 on error, but 1, to be
  347            consistent with command chaining rc.
  348    -  FIX: linux: case insensitive pattern match
  349            and text extraction did not work,
  350            esp. with xed or xex.
  351    -  CHG: improved scripting: combinations like
  352            sfk echo foo +setvar a=text
  353            sfk echo foo +setvar a +if ... setvar b=text
  354            sfk echo foo +xex ... +setvar b=1
  355            no longer require 'then' in many cases.
  356    -  add: Simple Expressions: tag [skip] at the start
  357            of a pattern now skips this text completely
  358            and does not count it as a hit.
  359    -  chg: archive file extensions: extended the list 
  360            of archive files recognized by -[q]arc by
  361            more office file extensions. 
  362            for full details type: sfk help opt
  363    -  chg: no longer activating archive file read 
  364            implicitely with most functions.
  365            use -[q]arc instead.
  366    -  fix: ftpserv, httpserv: unwanted listing of
  367            zip file contents instead of zip file itself.
  368    -  add: sfk addtovar, append text as lines to a variable
  369    -  add: variables: #(strpos(a,b)) and #(contains(a,b))
  370            now searches content of variable b within
  371            text of variable a.
  372    -  add: variables: #(begins(a,'word')) now tells if 
  373            text of variable a starts with word.
  374    -  chg: scripts: #(rsubstr(s,o,l)) now also supports
  375            offset/length beyond string length,
  376            producing an empty string instead of error.
  377    -  fix: scripts: #(substr(a,n)) with n beyond
  378            length of a stopped the script with
  379            "invalid variable expression".
  380            now it evaluates to an empty string.
  381    -  add: sfk variables: function #(numlines(a))
  382            to tell number of lines in variable a.
  383    -  add: sfk md5var, create md5 hash
  384            from sfk variable content.
  385    -  add: sfk difflines, show text lines that differ
  386            between two text files or variables
  387    -  add: sfk crc, crcgento, crccheck to create
  388            and verify crc32 checksums (the same as
  389            used in zip files).
  390    -  add: sfk list: now sets return code if any
  391            matching files or dirs were found.
  392    -  add: sfk web: now sets return code 1 on any error.
  393    -  add: sfk sft: now sets return code if any
  394            files were sent.
  395    -  add: del[tree] now sets return code on failure
  396    -  add: deltree now retries a failed folder deletion 
  397            2 times with a 100 msec wait inbetween.
  398    -  add: del[tree] option -stoponerr to stop chain
  399            execution if deletion fails
  400    -  chg: del[tree] now produces a warning
  401            if deletion is incomplete.
  402    -  fix: ftp client: 30 second wait after listing
  403            of directory or file download with some servers.
  404    -  fix: run: -printcmd was ignored.
  405    -  chg: zip reading: invalid zip file now produces
  406            a warning instead of an error.
  407    -  fix: xfind, replace functions, xrename: variables
  408            were not expanded for dir parameters.
  409    -  chg: sfk run: now shows a notice if no filenames
  410            are processed on chaining.
  411    -  chg: xed, xrename now ignore empty pattern parameters
  412            instead of stopping with an error.
  413            useful for scripting with optional parms.
  414    -  chg: command chaining: -verbose now shows a notice
  415            "chaining stops before ...: no data"
  416            if execution stops due to no data.
  417    -  chg: command chaining: no longer showing
  418            "chaining stops ...: no filenames"
  419            unless -verbose is given.
  420    -  chg: sfk batch out.sh under linux:
  421            added exit after sfk call.
  422    -  chg: sfk sft cput: if no files are sent
  423            then -quiet no longer tells anything.
  424    -  add: copy -flat2 to copy all files of a dir tree
  425            to an output folder with relative names.
  426    -  add: make-random-file -lock option, to keep
  427            a file locked for testing.
  428    -  fix: xfind with -justrc: chaining to if 
  429            was not possible if nothing was found.
  430    -  fix: xfind -showrc produced an error.
  431    -  add: ifnotexist, check if a file does not exist.
  432    -  add: swap, reverse chain text character order
  433    -  add: Windows: sfk clock: pressing enter
  434            now resets the relative time.
  435    -  add: Windows: sfk clock3 shows overall start time
  436            and, when pressing Enter, relative time since
  437            overall start in a second column.
  438    -  add: sfk clock2 to show just a relative time.
  439    -  add: sft client: multi file send: now showing
  440            total mb info.
  441    -  add: xed: option -tolines to force line output.
  442    -  fix: xed: listing of mem leaks if given filename
  443            cannot be read, or input too large.
  444    -  doc: end: xed -tolines reference
  445    -  doc: tofile: addtovar reference
  446    -  doc: sft: upload loop example.
  447    -  doc: index: reworked linux examples.
  448    -  doc: xfind: -showrc removed, wrong context.
  449    -  doc: filter: difflines reference.
  450    -  doc: help opt: new list of archive extensions
  451            and -qarc info
  452    -  doc: xed: reference to sfk swap
  453    -  doc: sfk help chain: data tunneling notice.
  454    internal:
  455    Revision 4:
  456    -  add: pingdiff, ping local net two times.
  457    Revision 3:
  458    -  add: SFK_CONFIG support for keepchain
  459    -  chg: windows: 32 bit time cleanup
  460    -  chg: timetToFileTime 64 bit rework
  461    -  add: chaining with sysinfo
  462    -  add: index: chaining of output filename
  463    -  add: script: chaining of output text
  464    Revision 2:
  465    -  add: option -slow for lower prio
  466    -  chg: cleanup of call by unified callLabel.
  467    -  fix: ftp client: correction of close/readline
  468            sequence in many cases
  469    -  add: ftp client: -timeout option
  470 
  471    1.9.2
  472    Initial Release:
  473    -  rel: 05.07.2018, Major Update
  474    -  sum: added sfk move to move files,
  475            getcol and tabcol to easily get a column
  476            from space or tab separated text.
  477            improvements of xed, getvar, scripting.
  478            fixed context cleanup of perline.
  479    -  chg: Syntax Change: sfk zip: no longer adds .zip
  480            to output filename if it contains any dot '.'.
  481            this allows to create zip files with any
  482            extension, like .odt or .jar.
  483            use option -zipext for the old behaviour.
  484    -  add: checkzip: option -md5 to show md5 checksums
  485            of zip file contents
  486    -  add: general option -more to pause output
  487            per page.
  488    -  add: xhexfind: option -context=n to show
  489            n bytes of context around results.
  490    -  add: sfk for: now supports counting down,
  491            negative start/end values, and optional
  492            step size.
  493    -  add: sfk zip: option -rel to strip top level
  494            folder name of filenames within zip.
  495    -  add: sfk xed: option -write to rewrite a given
  496            file with changed output.
  497    -  fix: sfk if: now supports expression starting
  498            with plus like: if "+a = +a" ...
  499    -  fix: perline: context and options were not
  500            fully reset between two lines, e.g.
  501            load files.txt +perline "list #qtext" -yes
  502            produced wrong output.
  503    -  add: sfk move: move files between two folders.
  504    -  add: sfk mkdir: option -forfile to create dir tree
  505            for a given filename
  506    -  add: scripts: #(substr(s,o,l)) now also supports
  507            variable (names) for o and l.
  508    -  add: scripts: #(rsubstr(s,o,l)) to get substring
  509            from the right side in left direction.
  510    -  add: sfk getcol, tabcol to get one column from
  511            whitespace or tab separated text.
  512            alias of sfk filter.
  513    -  add: sfk ping: option -every to ping every n sec.
  514    -  add: make-random-file: output folder creation.
  515    -  add: sfk times, alias for sfk list -times.
  516    -  add: list: now also accepts -times with 's'.
  517    -  add: sfk getvar: option -numline=n to get
  518            a line by number.
  519    -  add: zip/unzip: option -offtime to support
  520            file times which are one hour off.
  521    -  fix: xtext: with -to out\$file it produced error -4
  522            during simulation.
  523    -  fix: xtext: -to out\$file no longer writes :file
  524            headers into output files.
  525    -  fix: rand: missing range check.
  526    -  fix: ifexist file stop -all ... was not possible.
  527    -  fix: windows mingw g++ compile.
  528    -  chg: sfk if: improved "need a command" error info.
  529    -  doc: list: example how to keep only existing
  530            filenames from a list of filenames
  531    -  doc: count: example for filter/setvar/count.
  532    -  doc: copy: reference to move.
  533    -  doc: xtext: how to extract text starting at a line.
  534    -  doc: help var: new (r)substr behaviour.
  535    -  doc: addcr: example for current folder.
  536    -  doc: sfk times example.
  537    -  doc: sfk zip: example to unzip/zip an .odt file.
  538    internal:
  539    -  add: ping mynet: option -mac for linux
  540 
  541    1.9.1
  542    Revision 3:
  543    -  rel: 28.05.2018, Minor Update
  544    -  sum: sfk copy new option -flat to collect all into
  545            one folder with flat filenames. the sfk book
  546            is now available in print on Amazon.
  547            search "100 command line tools", or "sfk tutorial".
  548    -  add: copy: option -flat to copy all files into a
  549            single output dir without sub folders.
  550    -  doc: zipto: removed redundant example.
  551    -  doc: name: copy -flat example.
  552    -  doc: filter: -tolower/upper notice correction.
  553    Revision 2:
  554    -  rel: 17.04.2018, Minor Update
  555    -  sum: Better support for folder zipping if no sub folder
  556            contents are needed. Added script command ifexist
  557            to check if a file or folder exists.
  558    -  fix: sfk zip: created empty subdir entries with -nosub.
  559    -  add: sfk ifexist to check if a file or folder exists.
  560    -  fix: sfk getvar +tofile produced unwanted empty lines.
  561    -  doc: sfk zip: option -nosub
  562    -  doc: sfk list: option -nosub
  563    Initial Release:
  564    -  rel: 12.04.2018, Major Update
  565    -  sum: The Swiss File Knife can now create zip files
  566            and extract zip files, supporting UTF-8 unicode
  567            filenames and 64 bit contents with sizes over 2 gb.
  568            This allows highly flexible file selection with
  569            the full SFK syntax, like selecting all files
  570            changed today, or all files containing a searched
  571            pattern, and to pack them into a .zip directly.
  572    -  add: sfk zip, create zip file from folder.
  573    -  add: sfk zipto, zip selected file list.
  574    -  add: sfk unzip, list or extract zip file.
  575    -  add: sfk checkzip, verify zip file content.
  576    -  add: sfk run: quick confirm by sfk run. (with a dot)
  577    -  chg: sfk getdv unter windows no longer does a direct
  578            download, but shows an info text and then
  579            opens a web browser to download dview.
  580    -  add: scripts with -var: #(sys.slash) creates
  581            \ under windows, / under linux.
  582    -  add: require: option -nostop to continue chain.
  583    -  fix: sfk xfind -names ... +run etc. also
  584            printed the names to terminal.
  585    -  fix: rand: when chained with other random generator
  586            commands like make-random-file the seed was
  587            permanently reinitialized, producing redundant
  588            number sequences.
  589    -  fix: script: command chaining stopped after md5gento,
  590            make-random-file, snapto, joinlines, bin-to-src,
  591            deblank, color, fixfile, split, join, partcopy,
  592            setbytes, media, rename
  593    -  add: ftpserv: better windows 10 ftp client support
  594            by replying to OPTS UTF8 ON.
  595    -  chg: sfk num: little endian conversion is now always
  596            done as 32 bits, even with small numbers.
  597            use option -small for old behaviour.
  598    -  fix: sfk num: text output chaining did not work.
  599    -  add: sfk num: option -show x to print just a single
  600            format instead of all formats.
  601    -  chg: hexdump: now supports -off n with chain data.
  602    -  doc: xed: [2 chars] explained.
  603    -  doc: xed: example to swap char groups.
  604    -  doc: run: time measurement example.
  605    internal:
  606    revision 3:
  607    -  clp: code cleanup of udpdump.
  608    revision 2:
  609    -  add: general option -nosub2 to exclude subfolders
  610            and also hide subfolder names like in:
  611            sfk list -withdirs -nosub2 mydir5
  612    initial:
  613    -  chg: rework of tcpsend client.
  614 
  615    1.9.0
  616    Revision 2:
  617    -  rel: 12.03.2018, Minor Update
  618    -  sum: SFK for Windows now supports accent insensitive search
  619            and file selection according to the active codepage.
  620            added direct conversion between UTF-8 and Ansi.
  621    -  chg: behaviour change with wtoa, utoa:
  622            if chars fail to convert these now stop
  623            command chaining with return code 9.
  624            use option -nostop to continue processing
  625            with return code 1, without warning.
  626    -  add: windows: general option -deacc to search text
  627            and select files accent insensitive, meaning
  628            a == A == a_accent == A_accent.
  629    -  add: windows: listcodes support for -deacc.
  630    -  add: windows: sfk utftoansi, ansitoutf to convert
  631            8-bit text directly between your Ansi codepage
  632            and UTF-8 format.
  633    -  add: windows: wtoa, utoa return code support.
  634    -  add: listcodes: advanced option -codepage to select
  635            other codepages.
  636    -  fix: sfk inst: files with UTF-8 BOM header were changed
  637            incorrectly, producing a compile error near line 2.
  638            now the BOM is detected and text inserted after that.
  639    -  fix: sfk inst: line ending format lf vs. crlf is now kept.
  640    -  chg: sfk inst: now runs in simulation mode by default.
  641    -  doc: sfk filt, xex: -case with ref to help nocase.
  642    -  doc: sfk help unicode ref to utoa, atou.
  643    -  doc: main help ref to utoa.
  644    Initial Release:
  645    -  rel: 28.02.2018, Major Update
  646    -  sum: SFK for Windows now supports case insensitive search
  647            and file selection within the codepage of your
  648            Windows system. Depending on your codepage this allows
  649            to search umlauts, accents, cyrillic or greek characters
  650            case insensitive. Added functions to convert text 
  651            from and to UCS-2 wide character data.
  652    -  add: windows: case insensitive search and file selection
  653            within the codepage of your windows system.
  654            depending on your codepage this allows to search
  655            german umlauts, french accents, cyrillic and greek
  656            characters case insensitive.
  657    -  add: sfk help nocase, about case insensitive search.
  658    -  add: sfk listcodes, list all characters that can be searched
  659            case insensitive with your Windows codepage.
  660    -  add: sfk help chars, about locale specific characters.
  661    -  add: general option -nocasemin to match only latin
  662            chars a-z case insensitive without any accents.
  663    -  add: windows: sfk wtoa and atow, convert UCS-2 wide character 
  664            text to 8-bit Ansi codepage text and vice versa.
  665    -  add: sfk wtou and utow, convert UCS-2 wide character
  666            text to 8-bit UTF-8 text and vice versa.
  667    -  chg: windows: sfk chars output format changed,
  668            now showing both Ansi and OEM code, and character
  669            name where available, with a table header.
  670            use option -min for the old minimal format.
  671    -  fix: xex: option -case was ignored.
  672    -  fix: xtext, xfind: error -1 on output with files
  673            of 2 or 3 gbytes size. 
  674    -  add: xfind, xtext: now shows an improved progress indicator
  675            by default. use -quiet for old behaviour.
  676    -  add: sfk make-zero-file, create a file full of zero bytes
  677            to clean unused space on a filesystem.
  678    -  add: sfk status: new field layout=n to change SFKTray layout
  679            to 2, 4 or 9 slots display. requires SFKTray 1.0.2.
  680    -  add: windows: sfk prompt now converts input from dos
  681            to ansi codepage by default.
  682    -  add: windows: sfk script option -dos to load DOS/OEM
  683            codepage encoded script files.
  684    -  fix: windows: delayed terminal after filtering of text content
  685            with special characters.
  686    -  add: sfk strings: option -allchars to extract all printable
  687            chars from binary files.
  688    -  add: snapto: option -binallchars to extract all printable
  689            chars from binary files.
  690    -  add: sysinfo: display of SFK_CONFIG variable.
  691    -  add: xtext: display of written file with -to outdir\$file.
  692    -  fix: xtext: error message with -to outdir\$file
  693            as long as in simulation, without text output.
  694    -  chg: option -nocconv can now be used locally per command.
  695    -  dep: option -iso is deprecated/ignored.
  696    -  dep: option -umlauts is deprecated/ignored.
  697    -  doc: main help title changed
  698            from: The Swiss File Knife File Tree Processor
  699            to:   The Swiss File Knife Multi Function Tool.
  700    -  doc: sfk index, name now reference sfk home.
  701    -  doc: sfk setvar, load, storetext, stop in main help.
  702    -  doc: wtoa, wtou in main help.
  703    -  doc: xrep: notice to use [eol] to replace line endings.
  704    -  doc: filter: reference to help nocase, help unicode.
  705    -  doc: find, replace: reference to help nocase.
  706    -  doc: general option -binallchars
  707    -  doc: sfk ose: xfind help did not show expression syntax.
  708    -  doc: sfk ren: top reference to sfk renfile.
  709    -  doc: sfk copy: single file copy example.
  710    internal:
  711    -  chg: wtoa, atow now also support -isochars.
  712    -  chg: sfk ascii now also supports -codepage option.
  713    -  add: -codepage=a-o to set fixed ansi/oem codepage.
  714    -  add: -deacc for accent insensitive match.
  715    -  add: option -isoc as alias for -isochars.
  716    -  fix: xtext, xfind: iContextUsed 2 gb overflow caused error -1.
  717    -  chg: option -noacc undocumented, conflicts with -deacc.
  718 
  719    1.8.9
  720    Revision 2:
  721    -  rel: 10.01.2018, Minor Update
  722    -  sum: improved scripting error messages.
  723            better rename support in scripts.
  724            added filter option to skip first lines.
  725    -  add: sfk renfile, rename just a single file,
  726            for use in sfk scripts.
  727    -  add: filter -skipfirst=n to skip first n lines.
  728    -  chg: -nocol given locally at a command no longer
  729            changes the global color mode.
  730    -  chg: scripts: every command given like +cmdname
  731            can now be used in a script, no longer producing
  732            the error "does not support input chaining".
  733    -  add: sfk ren, xren: option -quiet to print nothing.
  734    -  add: sfk ren, xren: now tells number of changed files.
  735            option -[no]stat to show or hide statistics.
  736    -  add: sfk rmtree: option -quiet to print nothing,
  737            option -stat to show statistics with -quiet.
  738    -  fix: scripts: unknown commands were ignored silently,
  739            they now produce an error "unknown command".
  740    -  fix: -memlimit=3000 did not work.
  741    -  fix: sfk prompt help text.
  742    -  doc: improved prompt example.
  743    Initial Release:
  744    -  rel: 06.12.2017, Major Update
  745    -  sum: improved scripting with if begin endif blocks.
  746            better display of windows filenames on more codepages.
  747            added sfk rand to create random numbers,
  748            sfk prompt to allow user input.
  749            fixed xex return code handling
  750            and stop of script after sfk status.
  751    -  chg: windows: improved display of european filenames
  752            using non latin characters in the own codepage.
  753    -  chg: sfk batch tmp.bat: the example is now
  754            a short number game with status lights.
  755    -  add: scripting: +if expr +begin ... +endif
  756            to execute multiple commands as one block
  757            if given expression is true.
  758    -  add: scripting: easy check if a variable text contains
  759            a phrase by: if "#(contains(mystr,'text'))"
  760    -  add: if: support for if "rc<>1" to check if command rc
  761            is different to a value.
  762    -  add: sfk rand to create a random number.
  763    -  add: sfk prompt to ask for user input.
  764    -  chg: sfk status: no longer requires protocol version.
  765    -  fix: xex: return code was always 1 if previous
  766            command set it, no matter if xex found data.
  767    -  fix: fixfile: option -dewide displayed invalid date 1900...
  768            in simulation preview.
  769    -  fix: possible crash on setvar with empty value.
  770    -  fix: status: silent stop of command chain with
  771            some following commands like +list
  772    -  chg: windows OSE compile support for MSVC 14.
  773    -  chg: chaining from +tell to other commands like +setvar
  774            no longer requires +then.
  775    -  chg: improved tracing of variable expansion
  776            by -debug like +tell -debug "#(strlen(a))"
  777    -  doc: xex: how to check if a variable contains a word.
  778    -  doc: help var: how to search a variable in a variable.
  779    Revision 3:
  780    -  add: status: info that SFKTray 1.0.2 now supports
  781            layout=n to change number of slots shown.
  782    -  add: fromucs: option -ansi under Windows
  783    Revision 2:
  784    -  add: fromucs, toucs
  785    -  fix: fixfile: possible crash with some runtimes
  786            due to negative time given to strftime
  787 
  788    1.8.8
  789    Revision 2:
  790    -  rel: 21.09.2017, Minor Update
  791    -  sum: improved scripting with perline and getvar commands.
  792    -  add: perline: option -setvar x to put whole line text
  793            into variable x, for easy call of sub functions
  794            without parameters.
  795    -  add: getvar: option -line to extract first line
  796            matching a pattern. allows fast table lookups
  797            by a unique id if text is in a variable.
  798    -  add: hexdump: option -nolf for -pure format.
  799    -  doc: touch: how to touch a single dir.
  800    -  doc: filetime: how to list a single dir.
  801    -  doc: filter: link to getvar.
  802    Initial Release:
  803    -  rel: 04.09.2017, Major Update
  804    -  sum: added new GUI tool to display status infos
  805            by several lights in the system tray.
  806            added sfk status command to control these lights.
  807            added more flexible rename for files and folders.
  808    -  add: sfk xrename, a flexible rename for files and
  809            folder names, supporting full SFK Simple Expressions
  810            and many patterns per command.
  811    -  add: sfk status, send a colored status to the SFKTray
  812            Windows GUI tool for display.
  813    -  fix: fromnet: when used with command chaining
  814            it called following commands endless even with
  815            no input at all. now e.g. fromnet +filter ...
  816            will block until actual input data is received.
  817            use new option -nowait for old behaviour.
  818    -  fix: sfk web: input conflict error after some
  819            commands like setvar.
  820    -  chg: web download limit default is now 30 mb.
  821    -  add: sfk if: warning on "rc = 0" comparison which
  822            should be "rc=0".
  823    -  chg: syntax change: stat: further command name chars
  824            like "sfk statistics" are no longer supported.
  825    -  doc: sfk rename: -spat example and xrename reference.
  826 
  827    1.8.7
  828    Revision 2:
  829    -  rel: 15.07.2017, Minor Update
  830    -  sum: further improvements of http and udp message sending
  831            for easier scripting and convenience.
  832    -  chg: web: option -cweb renamed to -chainweb to avoid
  833            ambiguities with new commands. old -cweb is kept 
  834            for compatibility, but no longer documented.
  835    -  fix: sfk update: always told there was a newer version
  836            if new version info contained a trailing dot.
  837    -  add: cweb, an alias for web -nodump -quiet to call
  838            web url's quick without any output.
  839    -  add: udp, an alias for udpsend, but does not use
  840            any chain input data.
  841    -  add: cudp, call udp quickly without any output.
  842            same as udp -quiet.
  843    -  chg: wget: improved error message when trying https.
  844    -  chg: ftp server: SIZE command on non existing file
  845            now returns code 550 instead of 213.
  846    Initial Release:
  847    -  rel: 06.07.2017, Major Update
  848    -  sum: syntax corrections with sfk web, addhead and addtail.
  849            easier web automation in sfk scripts.
  850    -  CHG: SYNTAX CHANGE: command +web no longer uses chain input
  851            text by default, so commands like
  852               sfk filter ... +web ...
  853            will no longer work. use instead +tweb like
  854               sfk filter ... +tweb ...
  855            to explicitely read chain text, or set global option 
  856            -chainweb for the old behaviour. if you need your 
  857            old scripts to run without any changes, set environment 
  858            variable:
  859               SET SFK_CONFIG=chainweb
  860            reason: in sfk scripts +web is used most of the time 
  861            with an URL given as a parameter, but not read from
  862            chain text input, so permanent writing of +then web 
  863            was required. the new default makes web automation 
  864            scripts much easier.
  865    -  CHG: OUTPUT CHANGE: sfk addhead, addtail no longer
  866            add blanks between added words by default.
  867            use option -blank for the old behaviour.
  868    -  add: sfk addhead, addtail: command chaining support.
  869    -  add: sfk load: load text or data for further processing
  870            in a command chain. just like xex or filter,
  871            but more intuitive in scripts if you do not
  872            want to apply any filtering.
  873    -  add: web: -delay option to wait per request
  874    -  add: tweb, an alias of web that reads url's
  875            from chain text of previous command.
  876    -  add: global options -chainweb and -nochainweb to change
  877            +web chain input default.
  878    -  add: sort: -skiphead to keep table headers
  879    -  add: echo: option -joinline to reformat quoted multi line
  880            parameters with full trim. includes option -noblank
  881            which is now deprecated but kept for compatibility.
  882    -  add: sfk data: now supports multi line template in scripts.
  883    -  add: sfk data: support for $minute, $second
  884    -  add: doc: sfk help chain, script: reference to load.
  885    internal:
  886    -  fix: update: wrong curl syntax in case of download failure
  887    -  add: web -enc[ode] to encode blanks in urls
  888    -  add: mweb to run many commands quietly.
  889    -  add: copy: experimental move option.
  890    -  add: sort: option -tabcol 3,2,1
  891    -  add: setGeneralOption sim support
  892 
  893    1.8.6
  894    Revision 3:
  895    -  rel: 09.06.2017, Minor Update
  896    -  sum: detail improvements of calc and partcopy.
  897    -  add: sfk partcopy: option -fromtoinc to include
  898            the end offset in output.
  899    -  fix: sfk calc: expressions starting with a negative
  900            number were not supported.
  901    Revision 2:
  902    -  rel: 03.06.2017, Minor Update
  903    -  sum: fixed output formatting of multiple variables
  904            in the same command.
  905    -  FIX: Output Change: when formatting multiple variables
  906            in the same command like "#(3.3a) #(b) #(c)"
  907            then the formatting of the first variable was
  908            applied to all following. now only the given
  909            formatting per variable is applied.
  910    Initial Release:
  911    -  rel: 30.05.2017, Major Update
  912    -  sum: added easy variable output formatting
  913            like #(05i) which pads i to 5 characters
  914            with leading zeros. calculator now supports
  915            long expressions with many values.
  916    -  add: variable formatting on output in C-style
  917            notation like #(3.5varname) allowing to fill 
  918            text with leading blanks or zeros, and to limit 
  919            text to a given number of characters.
  920            for details see "sfk help var"
  921    -  add: sfk run, perline: now support ufile, upath
  922            or qufile, qupath to use unix slashes "/".
  923            useful to create html output.
  924    -  add: sfk calc: now supports long expressions
  925            with more than two values.
  926    -  chg: sfk calc: if chain input is used as #text
  927            it now fills in the whole text, no longer
  928            ignoring characters after first whitespace.
  929    -  fix: sfk touch: did not support chaining
  930            to following commands.
  931    -  fix: sfk httpserv: truncated bad formatted url's
  932            like "foo bar.txt" with unencoded blank
  933            as "foo". now these produce code 404 only.
  934    -  doc: sfk for: example how to copy files.
  935    -  doc: sfk perline: example how to create an
  936            html file listing images.
  937    -  chg: variable functions lpad, rpad are no longer
  938            documented but still supported. padding by
  939            the new C-style notation is easier.
  940    internal:
  941    Initial Release:
  942    -  web: url encoding of blanks
  943 
  944    1.8.5
  945    Revision 3:
  946    -  rel: 18.05.2017, Minor Update
  947    -  sum: sfk version output fixed, which was wrong
  948            especially under linux. ftpserv now shows
  949            the time of a client connect and disconnect.
  950    -  fix: sfk version: did not work esp. on linux.
  951    -  add: ftpserv: console connect time infos.
  952    Revision 2:
  953    -  rel: 06.05.2017, Minor Update
  954    -  sum: reduced ftp terminal output on error replies.
  955    -  chg: ftp server: no longer printing 5xx replies
  956            to terminal by default. added option -showerror
  957            to print them, except for 550 no such file.
  958    -  chg: ftp: protocol info is now shown only with 
  959            ftp(server) but not with sft commands.
  960    -  doc: xed: example how to replace a line by file content.
  961    Initial Release:
  962    -  rel: 26.04.2017, Major Update
  963    -  sum: fix of FTP server which blocked folder transfers
  964            with many ftp clients. rework of FTP server 
  965            and client for improved connectivity under Windows.
  966            improved scripting by for loop.
  967            added listle to list line end infos for text files.
  968            detail improvements of udpdump, list, setbytes.
  969    -  CHG: FTP Client Change under Windows: when using "sfk ftp" 
  970            with an sfk ftp server, via port 21, the client now
  971            uses plain FTP protocol by default to avoid firewall
  972            restrictions. use option -sft or SFK_CONFIG=usesft
  973            for the old behaviour if no firewall interferes.
  974    -  FIX: ftp server: transfer of folders with most ftp
  975            clients was blocked as CWD /mydir/ was not possible,
  976            since sfk 1.8.4. (side effect from dir traversal fix)
  977    -  fix: ftp server: unexpected close connection errors
  978            at client on QUIT command. QUIT now produces 
  979            a reply 221 before closing the connection.
  980    -  fix: ftp client: missing display of error reply from
  981            server on ftp put, mput, sel ... +ftp mput.
  982    -  add: ftp client: options -raw to force plain ftp
  983            and -sft to allow sft even via port 21.
  984    -  chg: ftp server: 5xx error replies are now always shown
  985            on console unless -quiet is given.
  986    -  chg: ftp: now tells protocol used (ftp/sft)
  987            on every file transfer.
  988    -  add: ftp server: display of verify in verbose mode.
  989    -  add: sfk for, like for var from s to e +... +endfor.
  990    -  add: sfk listle, list line end infos for text files.
  991    -  fix: sfk for windows: absolute paths with forward slash
  992            like /tmp were not detected with some commands like
  993            sfk sel /mydir +copy -ltarg /outdir
  994    -  add: sfk setbytes: option -repeat=n to repeat data
  995            n times.
  996    -  add: sfk list: option -abs to list full absolute paths.
  997    -  add: sfk udpdump: now accepts both "," or blank separator
  998            for multi ip mask.
  999    -  add: sfk udpdump: warning on wrong chars in ip mask.
 1000    -  chg: sfk view: changed some colors to look more like
 1001            the white color theme.
 1002    -  fix: sfk ask -abs used only -ab
 1003    -  doc: sfk replace: help now always lists the possible
 1004            shell return codes.
 1005    -  doc: sfk script: improved infos on parameter name collision.
 1006    -  doc: sfk filter: -format examples improved.
 1007    -  doc: sfk udpdump: examples for multi ip filtering.
 1008    -  doc: sfk xfind: windows return code handling example.
 1009    -  doc: sfk addcr/remcr: reference to listle.
 1010    -  doc: sfk ftpserv and ftp: help text rework.
 1011    internal:
 1012    -  opt: +setvar reuse of memory on same length.
 1013    -  fix: isAbsolutePath now also detects /tmp etc.
 1014 
 1015    1.8.4
 1016    Revision 2:
 1017    -  rel: 09.03.2017, Minor Update
 1018    -  sum: version command can now list standard windows 
 1019            version infos contained in .exe files.
 1020    -  chg: hextobin: no longer tells conversion statistics
 1021            when output is sent to another command
 1022    -  add: sfk version: support for windows binary version infos.
 1023    -  doc: encode/decode was not contained in dumphelp.
 1024    -  doc: main
 1025    Initial Release:
 1026    -  rel: 28.02.2017, Major Update
 1027    -  sum: security fix for windows ftp and http server.
 1028            added encoding and decoding of base64, hex
 1029            and url text formats. added setbytes command to set 
 1030            bytes directly in a file. improved scripting.
 1031    -  FIX: ftpserv and httpserv under Windows: with option -rw
 1032            write to a forbidden absolute path like C:\thedir\...
 1033            was not blocked.
 1034    -  CHG: Syntax Change: sfk ... +hexdump no longer expects
 1035            filename lists from previous commands, but only text
 1036            and binary data. use +hexfile or +fhexdump for the
 1037            old behaviour. this fixes issues that data sent
 1038            by the command chain was always misinterpreted
 1039            as filename lists, producing file open errors.
 1040    -  add: sfk clock, permanent display of absolute and
 1041            relative time.
 1042    -  add: sfk hexdump: automatic highlight of line end chars
 1043            with pure text input data.
 1044    -  fix: sfk for windows: repeated calls of sfk sft ... 
 1045            in scripts not possible.
 1046    -  add: sfk old, alias for sfk list -old to show oldest
 1047            files of a folder.
 1048    -  add: sfk small, alias for sfk list -small to show smallest
 1049            files of a folder.
 1050    -  add: sfk chars now supports command chain input.
 1051    -  add: ftp client in sft mode: display of 55x error replies
 1052            on file send, also without -verbose.
 1053    -  add: sfk encode and decode, convert data to and from
 1054            base64, hex or prefixed hex data.
 1055    -  add: sfk setbytes, write a byte sequence into a file
 1056            at a given offset.
 1057    -  del: the undocumented +text and +files keywords
 1058            in command chaining are no longer supported.
 1059    -  chg: sfk base: sfk sel ... +extract was not enabled.
 1060    -  chg: sfk base+xd: sfk sel ... +xrep demo was not enabled.
 1061    -  chg: echo -literal -noline ... followed by a command
 1062            accepting binary data, like xed, no longer appends (CR)LF.
 1063    -  add: echo -pure as an alias for -literal -noline,
 1064            to prepare chain input data as unchanged as possible.
 1065    -  doc: hexfind.
 1066    internal:
 1067    Revision 2:
 1068    -  add: fromclipw, toclipw.
 1069    -  chg: ver unified output using only chain.print.
 1070    Initial Release:
 1071    -  chg: unified path traversal check method.
 1072    -  fix: wsacleanup now called central once on process exit.
 1073 
 1074    1.8.3
 1075    Revision 3:
 1076    -  rel: 07.02.2017, Minor Update
 1077    -  sum: improved scripting, better error messages,
 1078            small bug fixes.
 1079    -  add: linux: main help info for more output columns.
 1080    -  add: diverse cannot open file error messages:
 1081            detail infos if filename contains cr/lf characters.
 1082    -  chg: sfk fromnet: renamed -nocache to -raw,
 1083            with -nocache still being supported.
 1084    -  add: udpsend is now chainable and may send chain input.
 1085    -  add: udpdump option -text for easy plain text display.
 1086    -  chg: Syntax Change: "sfk ... +list" no longer lists
 1087            the whole content of the current folder if previous
 1088            command did not send a list of filenames.
 1089            use "sfk ... +list ." instead.
 1090    -  chg: Syntax Change: sfk spell: no longer prints spelling
 1091            table by default, but shows help text.
 1092            use option -table for old behaviour.
 1093    -  add: sfk spell: support for chain text input,
 1094            spelled output is now prefixed by input words.
 1095    -  fix: sfk ... +toclip now tells an error instead of
 1096            blocking without notice if previous command
 1097            provides no input data.
 1098    -  fix: sfk colortest under windows did not restore
 1099            default color after test.
 1100    -  fix: xe: knxdump didn't work.
 1101    -  add: sfk ruler, to measure console text width.
 1102    -  ren: sfk diskspace primary name is now sfk space.
 1103            sfk fsinfo    primary name is now sfk filesys.
 1104            old names are supported for compatibility.
 1105    -  chg: scripting: these commands can now be called
 1106            without previous +then although they never use
 1107            chain input data: fromnet, color, make-random-file,
 1108            time, data, home, space, filesys, ruler.
 1109    -  doc: sfk getvar
 1110    Revision 2:
 1111    -  rel: 31.01.2017, Minor Update
 1112    -  sum: fix of unexpected stop of sfk scripts after commands
 1113            like xex or filter.
 1114    -  fix: unexpected stop of scripts after some commands
 1115            like web ... +xex ...
 1116    Initial Release:
 1117    -  rel: 21.01.2017, Major Update
 1118    -  sum: detail improvements for index file handling,
 1119            udp message sending, text filter, windows/linux
 1120            unified scripts. easier scripting by sfk tell which
 1121            never sends given text to following commands.
 1122            scripts can now call more nested sub functions.
 1123            bug fixes for run, tail, hexdump.
 1124    -  chg: option -upat now also support "/" for start of name
 1125            comparison as in -subdir :/tmp or -file :/tmp
 1126    -  chg: "sfk lindex" renamed to "sfk index" for easier use.
 1127            sfk lindex is still supported for compatibility.
 1128    -  add: "sfk iname" renamed to "sfk name" for easier use.
 1129            sfk iname is still supported for compatibility.
 1130    -  add: sfk udpsend: if a short ip is expanded the full
 1131            target ip is now shown. option -showip to force
 1132            showing of ip instead of host in sent info.
 1133    -  add: sfk ip: optional expansion of given short ip.
 1134            chaining support to pass ip text to other commands.
 1135    -  add: sfk filter -toupper, -tolower to convert a-z
 1136            to upper- or lowercase. does not convert any
 1137            special characters like accents or umlauts.
 1138    -  add: file selection option -tomake outdir\$base.ext
 1139            to select all files having no or an older counterpart
 1140            file in output folder outdir. see "sfk run" for 
 1141            an example of .wav to .mp3 conversion with ffmpeg.
 1142    -  fix: sfk udpsend, tonetlog could net send to different
 1143            targets in the same command chain or script.
 1144    -  add: sfk tell, same as echo but only prints to terminal
 1145            and never sends given text to following commands,
 1146            allowing scripts with fewer "+then" statements.
 1147    -  add: sfk data, create random test data records.
 1148    -  chg: reduced stack load per call and perline
 1149            allowing more nested calls.
 1150    -  fix: sfk dumphelp: now prints main help text first.
 1151    -  fix: sfk run with a single string containing # or $
 1152            did nothing.
 1153    -  fix: sfk tail under windows: handling of files > 2 GB
 1154            without option -altsize.
 1155    -  fix: sfk ... +hexdump unexpected dump of all files
 1156            of current folder.
 1157    -  chg: sfk knxdump, knxsend: GA support up to 31/7/255,
 1158            support to send to unicast address.
 1159    -  doc: updated list, run, help opt, echo/tell, xed,
 1160            filter, media, call, perline, dupfind
 1161    internal:
 1162    Revision 3:
 1163    -  add: setbytes.
 1164    -  fix: loadInput did not support non-color mode.
 1165    Revision 2:
 1166    -  add: ftpserv -usedir optional local limits.
 1167    Initial Release:
 1168    -  add: sfk web access: redirection notes now also show
 1169            from which url the redirect starts.
 1170    -  del: undocumented mindump, hexle.
 1171    -  chg: code move from submain to extmain,
 1172            reduced stack load per call from 140k to 50k.
 1173    -  add: sfk stacksize.
 1174    -  del: SFK_STRICT_MATCH name comparison.
 1175    -  chg: chain.print redirect on colany().
 1176    -  add: sfk encode/decode -url -hex -_hex ...
 1177    -  add: sfk filter -fromurl -fromhex -from_hex -frombase64
 1178    -  add: sfk filter -tourl -tohex -to_hex -tobase64
 1179 
 1180    1.8.2
 1181    Initial Release:
 1182    -  rel: 15.11.2016, Major Update
 1183    -  sum: sfk xfind, a powerful text search tool for the command line,
 1184            is now open source. compared to grep it features an easy, 
 1185            human readable pattern syntax based on the english language.
 1186            improved scripting by string functions like substr
 1187            and direct access to environment variables.
 1188    -  chg: sfk OSE: xfind, xhexfind, xtext and extract
 1189            are now open source.
 1190    -  add: sfk variable functions like #(substr(a,3,2)).
 1191            type "sfk help var" for details.
 1192    -  add: access to environment variables like #(env.varname).
 1193            type "sfk help var" for details.
 1194    -  chg: error text "incomplete replacement pattern"
 1195            changed to "imcomplete search pattern"
 1196            with improved correction hints.
 1197    -  fix: ftpserver with -usedir: empty listing in ftp clients
 1198            like cyberduck due to invalid rwx attribute infos.
 1199    -  fix: sfk ose compile: sfkbase.hpp did not define SFKOSE.
 1200    -  doc: xfind, xtext help reworked. improved "see also" list,
 1201            -enddir before -text is not needed, 
 1202            -text is listed before -pat.
 1203    -  doc: marked find, hexfind as basic commands
 1204            with reference to xfind, xhexfind.
 1205    internal:
 1206    -  add: list option -tomake enabling run (q)targ.
 1207 
 1208    1.8.1
 1209    Revision 4:
 1210    -  rel: 19.10.2016, Minor Update
 1211    -  sum: improved scripting with sfk if.
 1212            improved sfk count.
 1213    -  chg: sfk batch, sfk samp sfkbat with windows:
 1214            now using %~f0 instead of %0 to insert the
 1215            full absolute batch name including extensions.
 1216            this allows to type "mycmd" instead of
 1217            "mycmd.bat" to run a script.
 1218    -  add: sfk count: direct file reading support.
 1219    -  fix: sfk if: now also accepts expressions starting
 1220            like an option, like "-yes = -yes".
 1221    -  add: sfk tail: option -nowait to avoid blocking
 1222            in a command chain using +loop.
 1223    -  fix: progress info output after wget even with -quiet=2.
 1224    -  fix: sfk web: error message "wrong date/time" because
 1225            web server uses a different locale than client.
 1226            now there is only a notice if -verbose is used.
 1227    -  doc: sfk goto: infos about broken chain data flow.
 1228    -  doc: sfk batch: improved template batch with
 1229            help text display on empty parameters.
 1230    -  doc: wget info on -quiet=2.
 1231    Revision 3:
 1232    -  fix: sfk call, perline: error "missing or wrong script
 1233            parameter" with empty parameters.
 1234    -  fix: sfk run and other: empty parameter before dir
 1235            parameter list produced errors.
 1236    -  doc: info about no variable support with -bylist.
 1237    Revision 2:
 1238    -  ADD: sfk if with generic comparison of parameters,
 1239            variables, strings and numbers.
 1240    -  CHG: sfk script: empty parameters and variables
 1241            which are not surrounded by quotes are now removed.
 1242    -  add: sfk (x)hexfind, (x)replace, extract:
 1243            option -fullheader to show offset/length infos 
 1244            of found hits both with decimal and hexadecimal.
 1245    -  add: sfk goto, jump to a local label.
 1246    -  add: sfk label: option -var to enable variable use
 1247            for all following commands.
 1248    -  fix: xhexfind: results >= 4048 bytes were not printed.
 1249    -  add: xhexfind: option -maxdump=n to show only n bytes.
 1250    -  fix: call mylabel called mylabel2 if present.
 1251    -  fix: sfk update: did not detect new version
 1252            if 1.8.1 is compared with 1.8.1.2
 1253    -  fix: command chaining: when using short file selection
 1254            in a command then using long file selection in
 1255            following commands produced an error sometimes.
 1256    -  doc: sfk web: script example for access with
 1257            connection error handling.
 1258    -  doc: sfk label: info about global options.
 1259    -  doc: sfk script: info about -var after label.
 1260    Initial Release:
 1261    -  rel: 29.09.2016, Major Update
 1262    -  sum: added support for sfk variables, allowing to store data
 1263            from a command and to reuse it in other commands.
 1264            massive improved scripting.
 1265    -  ADD: sfk variables. type "sfk help var" for infos.
 1266            allows to store data produced by one command,
 1267            and to reuse it in another command like #(name)
 1268    -  ADD: sfk perline, run sfk commands per text input line.
 1269            similar to sfk run, but without starting an
 1270            external program, and with full access
 1271            to sfk variables produced by previous commands.
 1272    -  CHG: Syntax Change: sfk call, if: no longer clears
 1273            chain text after command. call will now flush
 1274            chain text before entering the label.
 1275    -  CHG: Syntax Change: sfk echo ... +call +xex
 1276            no longer passes text of echo as chain data
 1277            through to xex. use tcall for that.
 1278    -  CHG: Syntax Change: sfk gettext output changed with
 1279            text produced by storetext -append. more below.
 1280    -  add: sfk samp http, create an example script
 1281            for web access automation.
 1282    -  add: sfk xed, xex, xreplace, simple expressions:
 1283            [setvar name]...[endvar] to set an sfk variable,
 1284            [getvar name] to fill in data from an sfk variable.
 1285    -  add: sfk xed, simple expressions: keyword [ortext]
 1286            to search literals OR combined. can only be used
 1287            between literals.
 1288    -  add: sfk xed, simple expressions: keyword [keep]
 1289            to search text that should be kept in input,
 1290            without consume or replace.
 1291    -  add: sfk echo -lines to print every string as one line.
 1292    -  add: sfk xed: option -firsthit to use only first matching result.
 1293    -  add: sfk filter: option -line=n to read nth line from input.
 1294    -  add: sfk web: option -showreq, similar to -status.
 1295    -  add: sfk find: option -head=n to read only n lines.
 1296    -  add: sfk batch, alias of sfk samp sfkbat.
 1297            may also create .sh files under windows.
 1298    -  add: sfk xf, alias for xmlform.
 1299    -  fix: command chaining: unexpected drop of data when
 1300            chaining to echo, fromclip, var, gettext, getvar.
 1301            e.g. sfk echo foo +echo bar +tofile out.txt
 1302            dropped "foo", now it is printed.
 1303    -  add: command chaining: +tcall to consume text data
 1304            and +tend to return text data.
 1305    -  fix: sfk script -from begin: if script contained
 1306            a label begin2 this was called.
 1307    -  chg: sfk xex, xed: improved help examples, separate
 1308            examples for xex and xed.
 1309    -  chg: sfk samp sfkbat, sfkbash now use %0 or $0
 1310            instead of the script filename.
 1311    -  chg: Simple Expressions: now support zero length tokens
 1312            like in "/[start][0 chars][1 char]/"
 1313    -  chg: sfk samp: path now allowed in output file
 1314            with some language types.
 1315    -  add: sfk xex, xed: option -justrc to just set return code
 1316            without printing any output.
 1317    -  add: sfk ... +tovoid to drop current chain text.
 1318    -  add: sfk stop: optional text to print, info on rc 9
 1319            to stop a script from within call.
 1320            dump of chain text on rc >= 9.
 1321    -  add: sfk call: improved help text.
 1322    -  FIX: sfk script, call: missing parameters produced errors
 1323            or even crashed. now these are just empty.
 1324    -  fix: sfk ... +run was blocked with http:// url's.
 1325    -  fix: sfk web -status=... +xex didn't work.
 1326    -  fix: sfk echo: quoted multi line parms output
 1327            contained unwanted CR characters and trailing
 1328            blank line.
 1329    -  fix: sfk web: always produced rc 9.
 1330    -  fix: run -stoprc: did not work in a script.
 1331    -  fix: script: +if ... stop -all didn't work,
 1332            as "if" misinterpreted options.
 1333    -  fix: script: +stop no longer requires +then stop.
 1334    -  fix: gettext: missing linefeeds on terminal output
 1335            after multiple storetext -append. workarounds
 1336            appending with "\n" must be changed.
 1337    -  fix: sfk version -own +filter didn't work.
 1338    -  fix: expression part info didn't show # chars.
 1339    -  chg: sfk samp sfkbat, sfkbash new templates.
 1340    -  add: sfk knxsend: support for alternative IP group,
 1341            send of search request.
 1342    -  add: sfk knxdump: option -verbose to print search
 1343            response verbosely.
 1344    -  doc: extended echo help.
 1345    -  doc: reworked help for sfk script.
 1346    -  doc: help text for sfk then.
 1347    internal:
 1348    rev. 3:
 1349    -  add: env variable read support.
 1350    -  add: sfk calc: hex support.
 1351    initial:
 1352    -  chg: sfk run: option -strict
 1353    -  add: sfk ruler
 1354    -  del: sfk help firefox, samp firefox
 1355    -  add: cmd1 +to cmd2 draft
 1356    -  add: sfk rerun(loop) with output redirect.
 1357    -  add: sfk echo -indent=n to keep indentation in scripts.
 1358    -  fix: sfk echo ... +run ... chaining small improvement.
 1359    -  add: sft(serv) aliases sfs3, sf3.
 1360    -  fix: xex [setvar] no terminal output.
 1361    -  add: sfk variable use with most commands
 1362    -  chg: rework of perline, endper dropped
 1363 
 1364    1.8.0
 1365    Initial Release:
 1366    -  rel: 30.08.2016, Major Update
 1367    -  sum: sfk xed, a powerful stream text editor, is now free
 1368            and Open Source. it allows text and binary editing
 1369            with a simple, human readable syntax.
 1370            improved handling of multi line parameters in sfk scripts.
 1371    -  CHG: SYNTAX CHANGE: sfk script and sfk label:
 1372            quoted multi line parameters are now trimmed by default,
 1373            no longer requiring option -qtrim. see "sfk script"
 1374            for details. scripts requiring the old behaviour
 1375            may now need option -qraw after "sfk label".
 1376    -  chg: the SFK Mobile Booklet was fully reworked
 1377            and now covers the latest SFK version,
 1378            including an 38 page tutorial.
 1379            see stahlworks.com/pdf for details.
 1380    -  chg: sfk script: support for quoted multi line parms with
 1381            filter -rep, filter -form, web, wget, rename
 1382    -  add: sfk windows: clipsrc, clipphp, clipjava
 1383            to convert clipboard text to source code.
 1384            type "sfk bin-to-src" for documentation.
 1385    -  fix: linux: autoconf compile didn't work.
 1386    -  fix: compile: -DSFKOSE no longer necessary
 1387            to produce a Base/OSE binary info.
 1388    -  fix: web -nodump missing option documentation.
 1389    -  fix: xmlform: unwanted blank lines in output,
 1390            lost lines on some multi line tags.
 1391    internal:
 1392    -  chg: see sfk180 for dv build adaptions
 1393    -  add: sfk tohtml
 1394 
 1395    1.7.7
 1396    Initial Release:
 1397    -  rel: 29.07.2016, Major Update
 1398    -  sum: easy web data access with functions
 1399            sfk web, wfilter, xex. added sfk calc to do
 1400            simple calculations. improvements of wget,
 1401            ftpclient, udpdump, find, xfind, script.
 1402            diverse bug fixes.
 1403    -  inf: Compatibility Notice: sfk scripts with
 1404            quoted multi line parameters will change
 1405            their default behaviour in the future.
 1406            see "sfk label" options -qtrim and -qraw.
 1407    -  dep: Deprecation Warning: sfk webreq should
 1408            no longer be used. use sfk web instead.
 1409    -  chg: Compatibility Notice: sfk pause: no longer
 1410            sets a non zero shell return code if Esc
 1411            key is pressed (which was undocumented).
 1412    -  add: sfk update: check for SFK updates.
 1413    -  add: sfk pause: option -keyrc to set the
 1414            shell return code by ascii code of the
 1415            pressed key.
 1416    -  add: sfk filter: option -head=n to read only
 1417            first n lines of text files.
 1418    -  add: sfk filter: option -tail=n to read only
 1419            last n lines of text files.
 1420    -  add: web access support with sfk filter, xex.
 1421            help text examples for web access.
 1422    -  add: xe: sfk xreplace: option -astext to print
 1423            search/replace hits as plain text.
 1424    -  add: sfk web, send a simple web request.
 1425            new implementation replacing webreq.
 1426    -  add: sfk label: option -qtrim to strip whitespace
 1427            from quoted multiline parameters.
 1428    -  add: sfk ftpserv: support for environment variables
 1429            SFK_FTP_SUSER, SFK_FTP_SPW, SFK_FTP_SRUNPW
 1430            which are used only by sfk ftpserv, not sfk ftp.
 1431    -  add: sfk ftp: support for environment variables
 1432            SFK_FTP_CUSER, SFK_FTP_CPW which are used
 1433            only by sfk ftp, not sfk ftpserv.
 1434    -  add: main help now lists the most important
 1435            active environment variables.
 1436    -  add: full documentation of option -upat for using
 1437            unix patterns : and # under windows.
 1438    -  add: option -upat2 to use sfk linux wildcard %
 1439            instead of * under windows
 1440    -  add: sfk windows: set SFK_CONFIG=upat or upat2
 1441            support to use unix syntax in bash files
 1442    -  add: sfk for windows now also supports environment
 1443            variable option SFK_CONFIG=wildstar:c
 1444            to support a different wildcard character.
 1445    -  add: sfk calc, do a simple calculation of two values.
 1446    -  add: xfind, xhexfind: option -names to show just
 1447            the names of files containing hits.
 1448    -  fix: udpsend: no error on missing port or data.
 1449    -  fix: find: option -justrc produced blank lines.
 1450    -  add: udpdump: option -from now supports multiple
 1451            ip patterns separated by comma.
 1452    -  add: udpdump: option -notfrom.
 1453    -  add: wget: auto expansion of url to http://url
 1454    -  chg: ftp client: "server speaks sft" message
 1455            is now shown only with -verbose.
 1456    -  chg: ftp client: "using SFK_FTP_PW" message
 1457            is now shown only with -verbose.
 1458    -  add: wget: option -maxwait to stop on timeout.
 1459    -  opt: wget: no longer sends head request.
 1460    -  add: httpserv: -raw option to send content
 1461            without any reply headers.
 1462    -  fix: wget: download from a primitive server
 1463            that sends no http headers produced
 1464            incomplete first text line.
 1465    -  fix: sfk gettext: did not work in many cases.
 1466    -  chg: sfk if, sfk call: no longer consume chain text
 1467            without any output.
 1468    -  add: sfk fromclip -h: example how to append
 1469            clipboard changes to a logfile in a loop.
 1470    internal:
 1471    -  fix: resetLoadCaches no longer reset ConCache
 1472            as this conflicts with glblSFL.
 1473    -  add: -errtotext
 1474    -  add: setvar, getvar
 1475    -  add: perline, endper
 1476    -  del: dropped internal testfwrite.
 1477    -  chg: sfkmatch: renderOutput optim if no data.
 1478 
 1479    1.7.6
 1480    Revision 3:
 1481    -  fix: command chaining: failed to work with
 1482            sfk ping ... +then webreq +filter ...
 1483            now every chain i/o type after +then is ignored.
 1484    -  add: webreq: option -maxwait=n to wait up to
 1485            n msec for a connection.
 1486    -  add: ping: option -quiet=2 to not show OK etc.
 1487    Revision 2:
 1488    -  fix: sfk alias didn't work.
 1489    -  fix: sfk ping help: first example didn't work.
 1490    -  chg: improved help on output chaining error message.
 1491    -  chg: improved help on input chaining error message.
 1492    -  chg: udpsend: no longer use gethostbyname on pure ip's.
 1493    -  chg: global rework of all gethostbyname calls (setaddr).
 1494    -  add: sfk for windows: option -noesckey to disable
 1495            stop by escape key.
 1496    Initial Release:
 1497    -  add: windows: sfk fixfile, change bad filenames
 1498            and file times created by embedded devices
 1499            like PVR video recorders.
 1500    -  fix: sfk rename: crash on accent chars or umlauts
 1501            within the search pattern.
 1502    -  add: ftpserv: LIST now accepts -la, same as -a.
 1503    -  fix: ftpserv: endless loop on unexpected LIST option.
 1504    -  add: ftpserv: option -pasvport=n to define PASV port.
 1505    -  add: windows: option -nofo[llow] now skips symbolic links.
 1506    -  chg: windows: sfk lindex no longer follows symbolic links
 1507            by default. use -follow to change this.
 1508    -  add: sfk num: print number in all kinds of formats.
 1509    -  fix: sfk snapto: empty files were not added.
 1510    -  fix: sfk alias -list did not work.
 1511    -  fix: sfk xe: (x)replace -firsthit truncated file.
 1512    -  fix: improved cygwin compile.
 1513    -  add: sfk knxsend: now supports 4 bit messages.
 1514    -  add: sfk strlen: windows batch example.
 1515    internal:
 1516    Revision 3:
 1517    -  fix: Stringmap::remove: missing key cleanup.
 1518    -  run: option -upath for url support under windows.
 1519    Revision 2:
 1520    -  add: udpsend: option -raw for minimal send.
 1521    Initial Release:
 1522    -  add: option -yes+ and -clog command log support
 1523            with optional SFK_CMD_LOG configuration.
 1524 */
 1525 
 1526 // NOTE: if you change the source and create your own derivate,
 1527 // fill in the following infos before releasing your version of sfk.
 1528 #define SFK_BRANCH   ""
 1529 #define SFK_VERSION  "1.9.6.2" // ver_ and check the _PRE definition
 1530 #define SFK_FIXPACK  ""
 1531 #define SFK_INFO     "StahlWorks Technologies, http://stahlworks.com/"
 1532 #define SFK_PREVER   ""
 1533 
 1534 // in case of linking problems concerning libsocket etc.,
 1535 // you may out-comment this to compile without tcp support:
 1536 #ifndef USE_SFK_BASE
 1537  #define WITH_TCP
 1538  #define SFK_FTP_TIMEOUT "30" // seconds, as string
 1539 #endif // USE_SFK_BASE
 1540 
 1541 #define USE_SFT_UPDATE
 1542 #define SFK_CCDIRTIME   // copy over created dir time
 1543 
 1544 // should you get problems with fsetpos/fgetpos compile,
 1545 // activate this to disable zip/jar file content listing:
 1546 // #define NO_ZIP_LIST
 1547 
 1548 #ifdef SFK64
 1549  #define SFK_OS_BITSTR "-64"
 1550 #else
 1551  #define SFK_OS_BITSTR ""
 1552 #endif
 1553 
 1554 #if defined(__APPLE__) && !defined(MAC_OS_X)
 1555  #define MAC_OS_X
 1556 #endif
 1557 
 1558 #if !defined(VER_STR_OS) && defined(_WIN32)
 1559  #ifdef _WIN64
 1560   #define VER_STR_OS "windows-64"
 1561  #else
 1562   #define VER_STR_OS "windows-any"
 1563  #endif
 1564  #define _WIN32_WINNT 0x0400 // for copyFileEx
 1565  #define SFK_HAVE_ARC // win32
 1566 #endif
 1567 
 1568 #if !defined(VER_STR_OS) && defined(MAC_OS_X)
 1569   #ifdef MAC_OS_X_PPC
 1570    #define VER_STR_OS "mac-osx-ppc"
 1571    #define SFK_HAVE_ARC // mac-ppc
 1572   #endif
 1573   #ifdef MAC_OS_X_I686
 1574    #define VER_STR_OS "mac-osx-i686"
 1575    #define SFK_HAVE_ARC // mac-i686
 1576   #endif
 1577   #ifndef VER_STR_OS
 1578    #define VER_STR_OS "mac-osx"
 1579   #endif
 1580 #endif
 1581 
 1582 // generic ARM linux, "soft float" or float independent
 1583 #if !defined(VER_STR_OS) && defined(LINUX_ARM)
 1584  #define VER_STR_OS "linux-arm"
 1585  #define SFK_HAVE_ARC // linux-arm
 1586 #endif
 1587 
 1588 // if doing an explicite "hard float" compile
 1589 #if !defined(VER_STR_OS) && defined(LINUX_ARM_HF)
 1590  #define VER_STR_OS "linux-arm-hf"
 1591  #define SFK_HAVE_ARC // linux-arm-hf
 1592 #endif
 1593 
 1594 #if !defined(VER_STR_OS) && defined(SOLARIS)
 1595  #define VER_STR_OS "solaris"
 1596 #endif
 1597 
 1598 #if !defined(VER_STR_OS)
 1599  #ifdef SFK_LIB5
 1600   #define VER_STR_OS "linux-lib5"
 1601  #else
 1602   #define VER_STR_OS "linux-lib6" SFK_OS_BITSTR
 1603   #define SFK_LINUX_FULL
 1604  #endif
 1605  #ifdef SFK_LINUX_I686
 1606   #define SFK_HAVE_ARC // linux-lib5/lib6
 1607  #endif
 1608 #endif
 1609 
 1610 #define SFK_STRINGIFY(x) #x
 1611 #define SFK_TOSTRING(x) SFK_STRINGIFY(x)
 1612 
 1613 #if defined(_MSC_VER)
 1614  #define SFK_CPL_VER "vc" SFK_TOSTRING(_MSC_VER)
 1615  #if defined(_WIN64)
 1616   #define SFK_CPL_BITS ";x64"
 1617  #else
 1618   #define SFK_CPL_BITS ";x32"
 1619  #endif
 1620 #else
 1621  #define SFK_CPL_VER  ""
 1622  #define SFK_CPL_BITS ""
 1623 #endif
 1624 
 1625 #if defined(SFK_STATIC)
 1626  #define SFK_BUILD_INFO SFK_CPL_VER SFK_CPL_BITS "static"
 1627 #else
 1628  #define SFK_BUILD_INFO SFK_CPL_VER SFK_CPL_BITS ""
 1629 #endif
 1630 
 1631 #define VER_DAT_STR "date=" __DATE__
 1632 
 1633 #define  MTKTRACE_CODE
 1634 #include "sfkbase.hpp"
 1635 
 1636 const char *getPureSFKVersion() 
 1637 { 
 1638    if (strlen(SFK_FIXPACK) > 0)
 1639       return SFK_VERSION "." SFK_FIXPACK;
 1640    else
 1641       return SFK_VERSION; 
 1642 }
 1643 
 1644 const char *getShortOSName() 
 1645 {
 1646    #ifdef _WIN32
 1647    return "win";
 1648    #else
 1649     #ifdef MAC_OS_X
 1650     return "mac";
 1651     #else
 1652     return "linux";
 1653     #endif
 1654    #endif
 1655 }
 1656 
 1657 #ifdef VFILEBASE
 1658 #endif // VFILEBASE
 1659 
 1660 #ifdef USE_SFK_BASE
 1661  #define NO_VER_STR
 1662 #endif
 1663 
 1664 #ifndef NO_VER_STR
 1665 // binary version tag, is also parsed in version command.
 1666 // $version:vernum,shortid,full name,type$\0"
 1667  #ifndef SFK_VERTYPE
 1668   #define SFK_VERTYPE "Base" // version type
 1669  #endif
 1670  #ifndef SFK_VERTEXT
 1671   #define SFK_VERTEXT "/OSE" // open source edition
 1672  #endif
 1673 static const char *pszGlblVersion =
 1674    "$version:vernum=" SFK_VERSION ",name=sfk,title=Swiss File Knife,"
 1675    "info=" SFK_BUILD_INFO ","
 1676    "os=" VER_STR_OS ",type=" SFK_VERTYPE SFK_VERTEXT ",fix=" SFK_FIXPACK "," VER_DAT_STR "$\0";
 1677 static const char *pszGlblVerType = SFK_VERTYPE;
 1678 const char *getSFKVersion() { return pszGlblVersion; }
 1679 #endif
 1680 
 1681 #define SFK_BOTH_RUNCHARS
 1682 
 1683 #ifdef _WIN32
 1684  #define SFK_BOTH_RUNCHARS_HELP
 1685  #define SFK_MAP_ANSI_NEW // sfk189
 1686 #endif
 1687 
 1688 // #define SFK_STRICT_MATCH
 1689 #define SFKVAR // 1770
 1690 
 1691 #ifdef SFKINT2
 1692  #define SFKPRINTREDIR
 1693  #define SFKEXTERR
 1694 #endif
 1695 #define SFKNEWDUMP
 1696 
 1697 #ifndef USE_SFK_BASE
 1698  #define WITH_FN_INST
 1699 #endif // USE_SFK_BASE
 1700 
 1701 #ifdef _WIN32
 1702  #ifdef SFK_MEMTRACE
 1703   #include "memdeb.cpp"
 1704  #endif
 1705 #endif
 1706 #ifndef SFK_MEMTRACE
 1707 void sfkmem_checklist(const char *pszCheckPoint) { }
 1708 #endif
 1709 
 1710 #ifdef _WIN32
 1711 
 1712       char  glblNotChar     = '!';
 1713       char  glblRunChar     = '$';
 1714       char  glblWildChar    = '*';
 1715 
 1716 const char  glblPathChar    = '\\';
 1717 const char  glblWrongPChar  = '/';
 1718 const char *glblPathStr     = "\\";
 1719 const char *glblDubPathStr  = "\\\\";
 1720 const char *glblDotSlash    = ".\\";
 1721 
 1722 // Windows default compile has a stack size of 1 mb.
 1723 
 1724 #else
 1725 
 1726       char  glblNotChar     = ':';
 1727       char  glblRunChar     = '#';
 1728       char  glblWildChar    = '%';
 1729 
 1730 const char  glblPathChar    = '/';
 1731 const char  glblWrongPChar  = '\\';
 1732 const char *glblPathStr     = "/";
 1733 const char *glblDubPathStr  = "//";
 1734 const char *glblDotSlash    = "./";
 1735 
 1736 // Most linux have a stack larger than 1 mb, some smaller.
 1737 #define SFKBIGSTACK  // linux default
 1738 
 1739 #endif
 1740 
 1741 bool glblUPatMode = 0;
 1742 void *pGlblStartStack = 0;
 1743 
 1744 #if defined(SFKOFFICE)
 1745 bool bGlblOffice = 1;
 1746 #else
 1747 bool bGlblOffice = 0;
 1748 #endif
 1749 
 1750 extern struct CommandStats gs;
 1751 extern struct CommandStats cs;
 1752 extern struct CommandStats cspre;
 1753 extern struct CommandStats dummyCommandStats;
 1754 
 1755 void initWildCards()
 1756 {
 1757    #ifdef _WIN32
 1758    char c = '*'; // windows wildcard default
 1759    #else
 1760    char c = '%'; // linux wildcard default
 1761    #endif
 1762 
 1763    do {
 1764       char *pszWC = getenv("SFK_CONFIG"); // wildstar
 1765       if (!pszWC) break;
 1766       pszWC = strstr(pszWC, "wildstar:");
 1767       if (!pszWC) break;
 1768       pszWC += strlen("wildstar:");
 1769       if (!*pszWC) break;
 1770       c = *pszWC;
 1771    } while (0);
 1772    glblWildChar = c;
 1773 }
 1774 
 1775 bool isWildChar(char c) {
 1776    // 1770: optional dual wildchar support with windows.
 1777    //       was always the case with sfk linux.
 1778    return (c == '*' || c == glblWildChar) ? 1: 0;
 1779 }
 1780 bool isWildStr(char *p) {
 1781    if (strlen(p) != 1) return false;
 1782    return isWildChar(p[0]);
 1783 }
 1784 bool isNotChar(char c) {
 1785    #ifdef _WIN32
 1786    // sfk windows: support dual not char
 1787    return (c == '!' || c == glblNotChar) ? 1 : 0;
 1788    #else
 1789    // sfk linux: avoid possible conflicts with "!"
 1790    return (c == glblNotChar) ? 1 : 0;
 1791    #endif
 1792 }
 1793 bool isUniPathChar(char c) { // sfk183
 1794    if (c == glblPathChar) return true;
 1795    if (glblUPatMode==1 && c=='/') return true;
 1796    return false;
 1797 }
 1798 void setLinuxSyntax(int iLevel) {
 1799    glblUPatMode=1;
 1800    glblNotChar  = ':';
 1801    glblRunChar  = '#';
 1802    if (iLevel >= 2) {
 1803       glblWildChar = '%';
 1804    }
 1805 }
 1806 
 1807 void setTextColor(int n, bool bStdErr=0, bool bVerbose=0);
 1808 int perr(const char *pszFormat, ...);
 1809 int pwarn(const char *pszFormat, ...);
 1810 int pinf(const char *pszFormat, ...);
 1811 int containsWildCards(char *pszName);
 1812 num numFromSizeStr(char *psz, cchar *pszLoudInfo=0, bool bRelaxed=0);
 1813 int execToHtml(int imode, int iaspect, char *plist, char *pszOutFile);
 1814 int execPhraser(char *pszAll, char *pszSrc, int iNumRec);
 1815 extern const char *szGlblPhraseData;
 1816 int mySetFileTime(char *pszFile, num nTime);
 1817 int installSFK(char *pszFolder, bool byes);
 1818 bool strbeg(char *psz, cchar *pstart);
 1819 bool stribeg(char *psz, cchar *pstart);
 1820 int reformatjson(char *pinbuf, char *poutbuf, char *poutattr, int ioutmax);
 1821 
 1822 bool bGlblRandSeeded = 0;
 1823 
 1824 // ========== lowest level printf redirect ============
 1825 
 1826 #ifdef printf
 1827  #undef printf
 1828 #endif
 1829 int sfkprintf(const char *pszFormat, ...);
 1830 #define printf sfkprintf
 1831 
 1832 // ========== 64 bit abstraction layer begin ==========
 1833 
 1834 #ifdef SFK_W64
 1835 extern "C" 
 1836 {
 1837 int _fseeki64(FILE *stream, __int64 offset, int origin);
 1838 __int64 _ftelli64(FILE * _File);
 1839 }
 1840 #endif // SFK_W64
 1841 
 1842 char *numtostr(num n, int nDigits, char *pszBuf, int nRadix)
 1843 {
 1844    static char szBuf[100];
 1845    if (!pszBuf)
 1846         pszBuf = szBuf;
 1847 
 1848    #ifdef _WIN32
 1849    if (nRadix == 10)
 1850       sprintf(pszBuf, "%0*I64d", nDigits, n);
 1851    else
 1852       sprintf(pszBuf, "%0*I64X", nDigits, n);
 1853    return pszBuf;
 1854    #else
 1855    if (nRadix == 10)
 1856       sprintf(pszBuf, "%0*lld", nDigits, n);
 1857    else
 1858       sprintf(pszBuf, "%0*llX", nDigits, n);
 1859    return pszBuf;
 1860    #endif
 1861 }
 1862 
 1863 char *numtoa_blank(num n, int nDigits)
 1864 {
 1865    static char szBuf2[100];
 1866    #ifdef _WIN32
 1867    sprintf(szBuf2, "%*I64d", nDigits, n); // FIX: 1674
 1868    return szBuf2;
 1869    #else
 1870    sprintf(szBuf2, "%*lld", nDigits, n);  // FIX: 1674
 1871    return szBuf2;
 1872    #endif
 1873 }
 1874 
 1875 char *numtoa(num n, int nDigits, char *pszBuf) {
 1876    return numtostr(n, nDigits, pszBuf, 10);
 1877 }
 1878 
 1879 char *numtohex(num n, int nDigits, char *pszBuf) {
 1880    return numtostr(n, nDigits, pszBuf, 0x10);
 1881 }
 1882 
 1883 num atonum(char *psz)
 1884 {
 1885    #ifdef _WIN32
 1886    return _atoi64(psz);
 1887    #else
 1888    return atoll(psz);
 1889    #endif
 1890 }
 1891 
 1892 // atonum with support for decimal and 0x hex values
 1893 num myatonum(char *psz)
 1894 {
 1895    if (!strncmp(psz, "0x", 2)) {
 1896       #ifdef _MSC_VER
 1897       return _strtoui64(psz+2, 0, 0x10);
 1898       #else
 1899       return strtoull(psz+2, 0, 0x10);
 1900       #endif
 1901    } else {
 1902       return atonum(psz);
 1903    }
 1904 }
 1905 
 1906 mytime_t getSystemTime()
 1907 {
 1908    static mytime_t stSysTime = 0;
 1909    return mytime(&stSysTime);
 1910 }
 1911 
 1912 #ifdef _WIN32
 1913 int mygetpos64(FILE *f, num &rpos, char *pszFile)
 1914 {
 1915    fpos_t npos1;
 1916    if (fgetpos(f, &npos1))
 1917       return 9+perr("getpos failed on %s\n", pszFile);
 1918    rpos = (num)npos1;
 1919    return 0;
 1920 }
 1921 int mysetpos64(FILE *f, num pos, char *pszFile)
 1922 {
 1923    fpos_t npos1 = (fpos_t)pos;
 1924    if (fsetpos(f, &npos1))
 1925       return 9+perr("setpos failed on %s\n", pszFile);
 1926    return 0;
 1927 }
 1928 #else
 1929 // todo: check fpos_t size with linux, mac
 1930 int mygetpos64(FILE *f, num &rpos, char *pszFile)
 1931 {
 1932    fpos_t npos1;
 1933    if (fgetpos(f, &npos1))
 1934       return 9+perr("getpos failed on %s\n", pszFile);
 1935    #if defined(MAC_OS_X) || defined(SOLARIS)
 1936    rpos = (num)npos1;
 1937    #else
 1938    rpos = (num)npos1.__pos;
 1939    #endif
 1940    return 0;
 1941 }
 1942 int mysetpos64(FILE *f, num pos, char *pszFile)
 1943 {
 1944    // fetch "status" first
 1945    fpos_t npos1;
 1946    if (fgetpos(f, &npos1))
 1947       return 9+perr("getpos failed on %s\n", pszFile);
 1948    #if defined(MAC_OS_X) || defined(SOLARIS)
 1949    npos1 = (fpos_t)pos;
 1950    #else
 1951    npos1.__pos = (__off_t)pos;
 1952    #endif
 1953    if (fsetpos(f, &npos1))
 1954       return 9+perr("setpos failed on %s\n", pszFile);
 1955    return 0;
 1956 }
 1957 #endif
 1958 
 1959 // ========== 64 bit abstraction layer end ============
 1960 
 1961 // ====== SFK primitive function library begin ========
 1962 
 1963 char *ownIPList(int &rhowmany, uint nOptPort, const char *psep, int nmode);
 1964 
 1965 #if (defined(WITH_TCP) || defined(VFILENET) || defined(DV_TCP))
 1966 
 1967 bool bGlblTCPInitialized = 0;
 1968 
 1969 int prepareTCP()
 1970 {
 1971    if (!bGlblTCPInitialized)
 1972    {
 1973       bGlblTCPInitialized = 1;
 1974 
 1975       #ifdef _WIN32
 1976       WORD wVersionRequested = MAKEWORD(1,1);
 1977       WSADATA wsaData;
 1978       if (WSAStartup(wVersionRequested, &wsaData)!=0)
 1979          return 9+perr("WSAStartup failed\n");
 1980       #endif
 1981    }
 1982    return 0;
 1983 }
 1984 
 1985 void shutdownTCP()
 1986 {
 1987    if (!bGlblTCPInitialized)
 1988       return;
 1989 
 1990    bGlblTCPInitialized = 0;
 1991 
 1992    #ifdef _WIN32
 1993    WSACleanup(); // sfk1840 central, on process exit
 1994    #endif
 1995 }
 1996 
 1997 // all closesocket calls are redirected to:
 1998 void myclosesocket(SOCKET hsock, bool bread, bool bwrite)
 1999 {
 2000    int nmode = 0;
 2001 
 2002    if (bread  && !bwrite)
 2003       nmode = SHUT_RD;     // no more receptions
 2004    else
 2005    if (!bread && bwrite)
 2006       nmode = SHUT_WR;     // no more transmissions
 2007    else
 2008    if (bread  && bwrite)
 2009       nmode = SHUT_RDWR;   // no more transfers at all
 2010 
 2011    // signal the tcp stack that transmission stops,
 2012    // so the receiver side may receive remaining data.
 2013    shutdown(hsock, nmode);
 2014 
 2015    #ifdef _WIN32
 2016    closesocket(hsock);
 2017    #else
 2018    close(hsock);
 2019    #endif
 2020 }
 2021 
 2022 #endif // WITH_TCP or VFILENET
 2023 
 2024 // just close on a socket is not enough.
 2025 // myclosesocket also does the shutdown().
 2026 #define closesocket myclosesocket
 2027 
 2028 num getCurrentTime()
 2029 {
 2030    #ifdef _WIN32
 2031    return (num)GetTickCount();
 2032    #else
 2033    struct timeval tv;
 2034    gettimeofday(&tv, NULL);
 2035    return ((num)tv.tv_sec) * 1000 + ((num)tv.tv_usec) / 1000;
 2036    #endif
 2037 }
 2038 
 2039 // returns high resolution timer ticks, if available
 2040 num getCurrentTicks()
 2041 {
 2042    #ifdef _WIN32
 2043    LARGE_INTEGER val1;
 2044    QueryPerformanceCounter(&val1);
 2045    return val1.QuadPart;
 2046    #else
 2047    struct timeval tv;
 2048    gettimeofday(&tv, NULL);
 2049    return ((num)tv.tv_sec) * 1000 + ((num)tv.tv_usec) / 1000;
 2050    #endif
 2051 }
 2052 
 2053 num msecFromTicks(num nTicksDiff)
 2054 {
 2055    #ifdef _WIN32
 2056    LARGE_INTEGER val1;
 2057    QueryPerformanceFrequency(&val1);
 2058    num nMicroSecPeriod = val1.QuadPart;
 2059 
 2060    if (nMicroSecPeriod <= 0)
 2061       return 1;
 2062 
 2063    num nMSecDiff = nTicksDiff * 1000 / nMicroSecPeriod;
 2064 
 2065    if (nMSecDiff <= 0)
 2066       return 1;
 2067 
 2068    return nMSecDiff;
 2069    #else
 2070    return nTicksDiff;
 2071    #endif
 2072 }
 2073 
 2074 class StopWatch {
 2075 public:
 2076    StopWatch   (num &rAddTicksToThis)
 2077       : rOutputAddRef(rAddTicksToThis)
 2078       { nClStart = getCurrentTicks(); }
 2079   ~StopWatch   ( )
 2080       { rOutputAddRef += (getCurrentTicks() - nClStart); }
 2081 num nClStart, &rOutputAddRef;
 2082 };
 2083 
 2084 // copies a maximum of nMaxDst MINUS ONE chars,
 2085 // AND adds a zero terminator at pszDst (within nMaxDst range!).
 2086 // to use this like strncpy, always add +1 to nMaxDst.
 2087 // NOTE: if nMaxDst == 0, NO zero terminator is added.
 2088 void mystrcopy(char *pszDst, cchar *pszSrc, int nMaxDst) {
 2089    if (nMaxDst < 2) {
 2090       if (nMaxDst >= 1)
 2091          pszDst[0] = '\0';
 2092       return;
 2093    }
 2094    int nLen = strlen(pszSrc);
 2095    if (nLen > nMaxDst-1)
 2096       nLen = nMaxDst-1;
 2097    memcpy(pszDst, pszSrc, nLen);
 2098    pszDst[nLen] = '\0';
 2099 }
 2100 #define strcopy(dst,src) mystrcopy(dst,src,sizeof(dst)-10)
 2101 
 2102 int mystrwlen(const ushort *psz) {
 2103    int ilen=0;
 2104    while (psz[ilen])
 2105       ilen++;
 2106    return ilen;
 2107 }
 2108 
 2109 void mystrwcopy(ushort *pszDst, const ushort *pszSrc, int nMaxChr) {
 2110    if (nMaxChr < 2) {
 2111       if (nMaxChr >= 1)
 2112          pszDst[0] = '\0';
 2113       return;
 2114    }
 2115    int nChr = mystrwlen(pszSrc);
 2116    if (nChr > nMaxChr-1)
 2117       nChr = nMaxChr-1;
 2118    memcpy(pszDst, pszSrc, nChr*2);
 2119    pszDst[nChr] = '\0';
 2120 }
 2121 #define strwcopy(dst,src) mystrwcopy(dst,src,(int)(sizeof(dst)-10)/2)
 2122 
 2123 ushort *mystrwdup(ushort *psrc,int *pnchars=0)
 2124 {
 2125    int nchr=mystrwlen(psrc);
 2126    ushort *pres=new ushort[nchr+2]; // with tolerance
 2127    if (!pres) return 0;
 2128    memset(pres,0,(nchr+2)*2);
 2129    memcpy(pres,psrc,(nchr+1)*2); // with term
 2130    if (pnchars) *pnchars=nchr;
 2131    return pres;
 2132 }
 2133 
 2134 uchar *mymemdup(char *psz, int nlen)
 2135 {
 2136    uchar *p = new uchar[nlen+2];
 2137    if (!p) return p;
 2138    memcpy(p, psz, nlen);
 2139    p[nlen] = '\0'; // tolerance
 2140    return p;
 2141 }
 2142 
 2143 // remove blanks from right side of a string
 2144 void myrtrim(char *pszBuf) {
 2145    int nlen = strlen(pszBuf);
 2146    while (nlen > 0 && pszBuf[nlen-1] == ' ') {
 2147       pszBuf[nlen-1] = '\0';
 2148       nlen--;
 2149    }
 2150 }
 2151 
 2152 void myrtrimany(char *pszBuf, cchar *pfilt) {
 2153    int nlen = strlen(pszBuf);
 2154    while (nlen > 0 && strchr(pfilt, pszBuf[nlen-1])) {
 2155       pszBuf[nlen-1] = '\0';
 2156       nlen--;
 2157    }
 2158 }
 2159 
 2160 void skipUntil(char **pp, cchar *pdelim) {
 2161    char *p = *pp;
 2162    while (*p && !strchr(pdelim, *p))
 2163       p++;
 2164    *pp = p;
 2165 }
 2166 
 2167 void skipOver(char **pp, cchar *pdelim) {
 2168    char *p = *pp;
 2169    while (*p && strchr(pdelim, *p))
 2170       p++;
 2171    *pp = p;
 2172 }
 2173 
 2174 bool isws(char c)
 2175 {
 2176    if (c == ' ') return 1;
 2177    if (c == '\t') return 1;
 2178    return 0;
 2179 }
 2180 
 2181 bool iseol(char c) {
 2182    if (c == '\r') return 1;
 2183    if (c == '\n') return 1;
 2184    return 0;
 2185 }
 2186 
 2187 void skipToWhite(char **pp) { skipUntil(pp, " \t\r\n"); }
 2188 void skipWhite(char **pp)   { skipOver(pp, " \t\r\n");  }
 2189 
 2190 int nextLine(char **pp) {
 2191    char *p = *pp;
 2192    while (*p!=0 && *p!='\r' && *p!='\n')
 2193       p++;
 2194    if (*p!='\r' && *p!='\n')
 2195       return 1;
 2196    while (*p!=0 && (*p=='\r' || *p=='\n'))
 2197       *p++ = '\0';
 2198    *pp = p;
 2199    return 0;
 2200 }
 2201 
 2202 void swapchars(char *p, num nlen)
 2203 {
 2204    if (nlen < 2)
 2205       return;
 2206 
 2207    num nhalf = nlen / 2;
 2208    num imax  = nlen - 1;
 2209 
 2210    for (num i=0; i<nhalf; i++) 
 2211    {
 2212       char c = p[i];
 2213       p[i] = p[imax-i];
 2214       p[imax-i] = c;
 2215    }
 2216 }
 2217 
 2218 bool alldigits(char *psz) {
 2219    for (; *psz; psz++)
 2220       if (!isdigit(*psz))
 2221          return 0;
 2222    return 1;
 2223 }
 2224 
 2225 uint getFlexNum(char *psz, bool bAllHex)
 2226 {
 2227    /*
 2228       123
 2229       123.123.123.123
 2230       010101011101010
 2231       010.011.101.010
 2232       ab2f37e9
 2233       0x123
 2234       0o234
 2235       0b100
 2236    */
 2237 
 2238    // pass 1: get overall type
 2239    bool bbin=0,bdig=0,bxdig=bAllHex,bdots=0,bchar=0;
 2240    if (strBegins(psz, "0x"))
 2241       { bxdig=1; psz+=2; }
 2242    else
 2243    if (strBegins(psz, "0b"))
 2244       { bbin=1; psz+=2; }
 2245    else
 2246    if (strBegins(psz, "0t"))
 2247       { bchar=1; psz+=2; }
 2248    else
 2249    for (char *psz2=psz; *psz2; psz2++) {
 2250       char c = tolower(*psz2);
 2251       if (isdigit(c))
 2252          { bdig=1; continue; }
 2253       if (isxdigit(c))
 2254          { bxdig=1; continue; }
 2255       if (c=='0' || c=='1')
 2256          { bbin=1; continue; }
 2257       if (c=='.')
 2258          { bdots=1; continue; }
 2259       bchar=1;
 2260    }
 2261 
 2262    int ibase=1;
 2263    if (bxdig) ibase=16;
 2264    else
 2265    if (bdig)  ibase=10;
 2266    else
 2267    if (bbin)  ibase=2;
 2268 
 2269    // pass 2: collect number(s)
 2270    uint ncur=0,nout=0;
 2271    for (; *psz; psz++) {
 2272       char c = tolower(*psz);
 2273       if (c=='.') {
 2274          nout = nout + ncur;
 2275          nout = nout * 256;
 2276          ncur = 0;
 2277          continue;
 2278       }
 2279       if (bchar) {
 2280          ncur = ncur * 256;
 2281          ncur = ncur + (uint)(*psz);
 2282          continue;
 2283       }
 2284       ncur = ncur * ibase;
 2285       switch (c) {
 2286          case '0': case '1': case '2': case '3': case '4':
 2287          case '5': case '6': case '7': case '8': case '9':
 2288             ncur = ncur + ((uint)c - '0');
 2289             continue;
 2290          case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
 2291             ncur = ncur + 10 + ((uint)c - 'a');
 2292             continue;
 2293       }
 2294    }
 2295 
 2296    nout = nout + ncur;
 2297 
 2298    return nout;
 2299 }
 2300 
 2301 double getcalcval(char *psz, char **pcont)
 2302 {
 2303    if (!strncmp(psz, "0x", 2)) {
 2304       // must be unsigned long integer
 2305       #ifdef _MSC_VER
 2306       return (double)_strtoui64(psz+2, pcont, 0x10);
 2307       #else
 2308       return (double)strtoull(psz+2, pcont, 0x10);
 2309       #endif
 2310    } else {
 2311       // accept signed double
 2312       return strtod(psz, pcont);
 2313    }
 2314 }
 2315 
 2316 extern cchar *pszGlblBlank;
 2317 
 2318 int sfkcalc(double &r, char *pszFrom, char **ppNext, int iLevel, bool bStopPM=0)
 2319 {
 2320    double v1=0.0,v2=0.0;
 2321 
 2322    if (cs.debug) printf("%.*s[%s]\n",iLevel*2,pszGlblBlank,pszFrom);
 2323 
 2324    char *pszNext=0;
 2325    int   iOwnBra=0;
 2326 
 2327    skipWhite(&pszFrom); // sfk1962
 2328 
 2329    if (isdigit(*pszFrom)!=0 || *pszFrom=='-') {
 2330       v1=getcalcval(pszFrom,&pszNext);
 2331       pszFrom=pszNext;
 2332    } else if (cs.brackets==1 && *pszFrom=='(') {
 2333       pszFrom++;
 2334       iOwnBra++;
 2335       if (sfkcalc(v1,pszFrom,&pszNext,iLevel+1))
 2336          return 9;
 2337       pszFrom=pszNext;
 2338    }
 2339      else return 9+perr("invalid: %s\n",pszFrom);
 2340 
 2341    bool bstop=0;
 2342    while (*pszFrom!=0 && bstop==0)
 2343    {
 2344       skipWhite(&pszFrom); // sfk1962
 2345       if (*pszFrom==0)     // sfk1962
 2346          break;
 2347 
 2348       if (cs.debug) printf("%.*s[%s]\n",iLevel*2,pszGlblBlank,pszFrom);
 2349 
 2350       // get * / + -
 2351       char c = *pszFrom++;
 2352       switch (c)
 2353       {
 2354          case ')':
 2355             if (!cs.brackets)
 2356                return 9+perr("invalid: %s\n",pszFrom-1);
 2357             if (cs.debug) printf("%.*s) %f\n",iLevel*2,pszGlblBlank,v1);
 2358             if (iOwnBra-- <= 0) {
 2359                bstop=1;
 2360                pszFrom--;
 2361             }
 2362             continue;
 2363          case '*':
 2364             if (cs.brackets==1 && *pszFrom=='(') {
 2365                pszFrom++;
 2366                iOwnBra++;
 2367                if (sfkcalc(v2,pszFrom,&pszNext,iLevel+1))
 2368                   return 9;
 2369             } else {
 2370                v2=getcalcval(pszFrom,&pszNext);
 2371             }
 2372             if (cs.debug) printf("%.*s%f * %f = %f\n",iLevel*2,pszGlblBlank,v1,v2,v1*v2);
 2373             v1 *= v2;
 2374             pszFrom=pszNext;
 2375             continue;
 2376          case '/':
 2377             if (cs.brackets==1 && *pszFrom=='(') {
 2378                pszFrom++;
 2379                iOwnBra++;
 2380                if (sfkcalc(v2,pszFrom,&pszNext,iLevel+1))
 2381                   return 9;
 2382             } else {
 2383                v2=getcalcval(pszFrom,&pszNext);
 2384             }
 2385             if (v2 == 0.0)
 2386                return 9+perr("division by zero: %s\n",pszFrom);
 2387             if (cs.debug) printf("%.*s%f / %f = %f\n",iLevel*2,pszGlblBlank,v1,v2,v1/v2);
 2388             v1 /= v2;
 2389             pszFrom=pszNext;
 2390             continue;
 2391          case '+':
 2392             if (bStopPM) {
 2393                pszFrom--;
 2394                bstop=1;
 2395                continue;
 2396             }
 2397             if (sfkcalc(v2, pszFrom, &pszNext, iLevel+1, 1))
 2398                return 9;
 2399             if (cs.debug) printf("%.*s%f + %f = %f\n",iLevel*2,pszGlblBlank,v1,v2,v1+v2);
 2400             v1 += v2;
 2401             pszFrom=pszNext;
 2402             continue;
 2403          case '-':
 2404             if (bStopPM) {
 2405                pszFrom--;
 2406                bstop=1;
 2407                continue;
 2408             }
 2409             if (sfkcalc(v2, pszFrom, &pszNext, iLevel+1, 1))
 2410                return 9;
 2411             if (cs.debug) printf("%.*s%f - %f = %f\n",iLevel*2,pszGlblBlank,v1,v2,v1-v2);
 2412             v1 -= v2;
 2413             pszFrom=pszNext;
 2414             continue;
 2415          default:
 2416             return 9+perr("invalid: %s\n",pszFrom-1);
 2417       }
 2418    }
 2419 
 2420    r=v1;
 2421    if (ppNext) *ppNext=pszFrom;
 2422 
 2423    return 0;
 2424 }
 2425 
 2426 struct tm *mylocaltime(mytime_t *ptime)
 2427 {
 2428    #ifdef SFK_W64
 2429    return _localtime64(ptime);
 2430    #else
 2431    return localtime(ptime);
 2432    #endif
 2433 }
 2434 
 2435 struct tm *mygmtime(mytime_t *ptime)
 2436 {
 2437    #ifdef SFK_W64
 2438    return _gmtime64(ptime);
 2439    #else
 2440    return gmtime(ptime);
 2441    #endif
 2442 }
 2443 
 2444 #ifdef _WIN32
 2445  #define getcwd _getcwd
 2446  #define rmdir  _rmdir
 2447 #endif
 2448 
 2449 bool bGlblGotToMake = 0;
 2450 
 2451 uchar *newBitField(int iTotalEntries)
 2452 {
 2453    int iByteSize = (iTotalEntries / 8) + 1;
 2454    int iTolerance= 4;
 2455    uchar *pField = new uchar[iByteSize+iTolerance];
 2456    if (!pField)
 2457       return 0;
 2458    memset(pField, 0, iByteSize+iTolerance);
 2459    int *pSize = (int *)pField;
 2460    *pSize = iTotalEntries;
 2461    return pField;
 2462 }
 2463 // can be freed by delete [] pField
 2464 // sfkSetBit, sfkGetBit see sfkbase.hpp
 2465 
 2466 UTF8Codec::UTF8Codec(char *pOptInData, int iOptionalInputLength)
 2467 {
 2468    memset(this, 0, sizeof(*this));
 2469 
 2470    if (pOptInData)
 2471       init(pOptInData, iOptionalInputLength);
 2472 }
 2473 
 2474 void UTF8Codec::init(char *p, int ilen)
 2475 {
 2476    icur = 0;
 2477    psrc = (uchar*)p;
 2478 
 2479    if (ilen < 0)
 2480       imax = strlen(p);
 2481    else
 2482       imax = ilen;
 2483 }
 2484 
 2485 int UTF8Codec::readRaw()
 2486 {
 2487    if (icur >= imax)
 2488       return 0;
 2489    return psrc[icur++] & 0xFF;
 2490 }
 2491 
 2492 int UTF8Codec::readSeq()
 2493 {
 2494    int c = readRaw();
 2495    return ((c & 0xC0) == 0x80) ? (c & 0x3F) : -1;
 2496 }
 2497 
 2498 bool UTF8Codec::hasChar() { return (icur < imax) ? 1 : 0; }
 2499 
 2500 bool UTF8Codec::eod() { return (icur >= imax) ? 1 : 0; }
 2501 
 2502 int  UTF8Codec::validSeqLen(char *pszSrc, int iMaxSrc)
 2503 {
 2504    UTF8Codec obj;
 2505    return obj.validSeqLenInt(pszSrc, iMaxSrc);
 2506 }
 2507 
 2508 int  UTF8Codec::validSeqLenInt(char *pszSrc, int iMaxSrc)
 2509 {
 2510    if (iMaxSrc < 2)
 2511       return 0;
 2512 
 2513    init(pszSrc, iMaxSrc);
 2514 
 2515    if (icur >= imax)
 2516         return 0;
 2517 
 2518    int c = readRaw();
 2519 
 2520    int iold = icur;
 2521 
 2522    if ((c & 0x80) == 0)
 2523       return 0;
 2524 
 2525    int c1,c2,c3,n;
 2526 
 2527    if ((c & 0xE0) == 0xC0) {
 2528       if ((c1 = readSeq()) < 0)
 2529          return 0;
 2530       n = ((c & 0x1F) << 6) | c1;
 2531       if (n >= 128)
 2532          return 2;
 2533       return 0;
 2534    } else if ((c & 0xF0) == 0xE0) {
 2535       if ((c1 = readSeq()) < 0)
 2536          return 0;
 2537       if ((c2 = readSeq()) < 0)
 2538          return 0;
 2539       n = ((c & 0x0F) << 12) | (c1 << 6) | c2;
 2540       if (n >= 0x800 && (n < 0xD800 || n > 0xDFFF))
 2541          return 3;
 2542       return 0;
 2543    } else if ((c & 0xF8) == 0xF0) {
 2544       if ((c1 = readSeq()) < 0)
 2545          return 0;
 2546       if ((c2 = readSeq()) < 0)
 2547          return 0;
 2548       if ((c3 = readSeq()) < 0)
 2549          return 0;
 2550       return 4;
 2551    }
 2552 
 2553    return 0;
 2554 }
 2555 
 2556 uint UTF8Codec::nextChar()
 2557 {
 2558    if (icur >= imax)
 2559       return 0;
 2560 
 2561    if (bdecodexml==1 && psrc[icur]=='&')
 2562    {
 2563       // decode xml predefined entities
 2564       int irem=imax-icur;
 2565       if (irem>=6 && !strncmp((char*)psrc+icur, "&quot;", 6))
 2566         { icur+=6; return '\"'; }
 2567       if (irem>=6 && !strncmp((char*)psrc+icur, "&apos;", 6))
 2568         { icur+=6; return '\''; }
 2569       if (irem>=5 && !strncmp((char*)psrc+icur, "&amp;", 5))
 2570         { icur+=5; return '&'; }
 2571       if (irem>=4 && !strncmp((char*)psrc+icur, "&lt;", 4))
 2572         { icur+=4; return '<'; }
 2573       if (irem>=4 && !strncmp((char*)psrc+icur, "&gt;", 4))
 2574         { icur+=4; return '>'; }
 2575       icur++;
 2576       return '&';
 2577    }
 2578 
 2579    int c = readRaw();
 2580 
 2581    int iold = icur;
 2582 
 2583    if (bkeeputf == 1 || (c & 0x80) == 0)
 2584       return c;
 2585 
 2586    banychars = 1;
 2587 
 2588    int c1,c2,c3,n;
 2589 
 2590    do {
 2591       if ((c & 0xE0) == 0xC0) {
 2592          if ((c1 = readSeq()) < 0)
 2593             break;
 2594          n = ((c & 0x1F) << 6) | c1;
 2595          if (n >= 128)
 2596             return n;
 2597       } else if ((c & 0xF0) == 0xE0) {
 2598          if ((c1 = readSeq()) < 0)
 2599             break;
 2600          if ((c2 = readSeq()) < 0)
 2601             break;
 2602          n = ((c & 0x0F) << 12) | (c1 << 6) | c2;
 2603          if (n >= 0x800 && (n < 0xD800 || n > 0xDFFF))
 2604             return n;
 2605       } else if ((c & 0xF8) == 0xF0) {
 2606          if ((c1 = readSeq()) < 0)
 2607             break;
 2608          if ((c2 = readSeq()) < 0)
 2609             break;
 2610          if ((c3 = readSeq()) < 0)
 2611             break;
 2612          return (((c & 0x0F) << 18) | (c1 << 12) | (c2 << 6) | c3) + 0x10000;
 2613       }
 2614    }
 2615    while (0);
 2616 
 2617    // invalid sequence:
 2618    bbadchars = 1;
 2619 
 2620    // return undecoded text
 2621    icur = iold;
 2622 
 2623    return c;
 2624 }
 2625 
 2626 bool UTF8Codec::isValidUTF8(char *psz)
 2627 {
 2628    UTF8Codec utf(psz);
 2629 
 2630    while (utf.nextChar());
 2631 
 2632    if (utf.banychars==0)
 2633       return 0;
 2634    if (utf.bbadchars==1)
 2635       return 0;
 2636 
 2637    return 1;
 2638 }
 2639 
 2640 int UTF8Codec::toutf8(char *pszOut, int iMaxOut, uint ch)
 2641 {
 2642    uint c = ch;
 2643    int len = 0;
 2644    uint first = 0;
 2645  
 2646    if (c < 0x80) {
 2647       first = 0;
 2648       len = 1;
 2649    }
 2650    else if (c < 0x800) {
 2651       first = 0xc0;
 2652       len = 2;
 2653    } else if (c < 0x10000) {
 2654       first = 0xe0;
 2655       len = 3;
 2656    } else if (c < 0x200000) {
 2657       first = 0xf0;
 2658       len = 4;
 2659    } else if (c < 0x4000000) {
 2660       first = 0xf8;
 2661       len = 5;
 2662    } else {
 2663       first = 0xfc;
 2664       len = 6;
 2665    }
 2666 
 2667    if (len >= iMaxOut)
 2668       return 0;
 2669 
 2670    if (!pszOut)
 2671       return 0;
 2672 
 2673    for (int i = len - 1; i > 0; i--)
 2674    {
 2675       pszOut[i] = (char)((c & 0x3f) | 0x80);
 2676       c >>= 6;
 2677    }
 2678    pszOut[0] = c | first;
 2679  
 2680    return len;
 2681 }
 2682 
 2683 int UTF8Codec::toutf8(char *pszOut, int iMaxOut, char *pszSrc, bool bSafe)
 2684 {
 2685    if (iMaxOut < 2)
 2686       return 0;
 2687 
 2688    *pszOut = '\0';
 2689 
 2690    char *pDstCur = pszOut;
 2691    char *pDstMax = pszOut+iMaxOut;
 2692    int   iSrcLen = strlen(pszSrc);
 2693 
 2694    uchar *pSrcCur = (uchar*)pszSrc;
 2695    uchar *pSrcMax = pSrcCur + iSrcLen;
 2696 
 2697    UTF8Codec obj;
 2698 
 2699    while (pSrcCur<pSrcMax && *pSrcCur != 0)
 2700    {
 2701       if (bSafe) {
 2702          // skip existing utf8 sequences in mixed text
 2703          int l = obj.validSeqLenInt((char*)pSrcCur, pSrcMax-pSrcCur);
 2704          if (l > 0) {
 2705             if (pDstCur+l >= pDstMax)
 2706                break;
 2707             memcpy(pDstCur, pSrcCur, l);
 2708             pDstCur += l;
 2709             pSrcCur += l;
 2710             continue;
 2711          }
 2712       }
 2713 
 2714       uint c = *pSrcCur;
 2715       int  n = toutf8(pDstCur, pDstMax-pDstCur, c);
 2716       if (n < 1)
 2717          break;
 2718       if (pDstCur >= pDstMax)
 2719          break;
 2720       pDstCur += n;
 2721       pSrcCur++;
 2722    }
 2723 
 2724    if (pDstCur >= pDstMax)
 2725       pDstCur = pDstMax-1;
 2726 
 2727    *pDstCur = '\0';
 2728 
 2729    return pDstCur - pszOut;
 2730 }
 2731 
 2732 // ====== SFK primitive function library end   ========
 2733 
 2734 // =========== Profiling ==========
 2735 
 2736 #ifdef SFK_PROFILING
 2737 
 2738 // CRITICAL: static constructor sequence
 2739 // <-> StaticPerformancePoint
 2740 StaticPerformanceStats glblPerfStats;
 2741 
 2742 StaticPerformanceStats::StaticPerformanceStats( )
 2743 {
 2744    memset(this, 0, sizeof(*this));
 2745 }
 2746 
 2747 void StaticPerformanceStats::addPoint(StaticPerformancePoint *pPoint)
 2748 {
 2749    if (iClPoints >= MAX_PERF_POINTS)
 2750    {
 2751       fprintf(stdout, "ERROR: too many profiling points\n");
 2752       fprintf(stderr, "ERROR: too many profiling points\n");
 2753       return;
 2754    }
 2755 
 2756    apClPoints[iClPoints++] = pPoint;
 2757 }
 2758 
 2759 int StaticPerformanceStats::numberOfPoints( )
 2760    { return iClPoints; }
 2761 
 2762 StaticPerformancePoint *StaticPerformanceStats::getPoint(int iIndex)
 2763    { return apClPoints[iIndex]; }
 2764 
 2765 // CRITICAL: static constructor sequence
 2766 // <-> StaticPerformanceStats
 2767 StaticPerformancePoint::StaticPerformancePoint(const char *pszID,
 2768    const char *pszFile, int iTraceLine)
 2769 {
 2770    pszClID = pszID;
 2771    pszClFile = pszFile;
 2772    iClTraceLine = iTraceLine;
 2773    iClHits = 0;
 2774    iClTotalTime = 0;
 2775    iClSubTimes = 0;
 2776    glblPerfStats.addPoint(this);
 2777 }
 2778 
 2779 void StaticPerformancePoint::blockEntry( )
 2780    { iClHits++; }
 2781 
 2782 void StaticPerformancePoint::blockExit(int iElapsedTicks)
 2783    { iClTotalTime += iElapsedTicks; }
 2784 
 2785 DynamicPerformancePoint::DynamicPerformancePoint(StaticPerformancePoint *pStaticPoint)
 2786 {
 2787    nClEntryTickCount = getPerfCnt();
 2788    pClStaticPoint = pStaticPoint;
 2789 
 2790    // enter next level
 2791    pClStaticParent = glblPerfStats.pClCurrentPoint;
 2792    glblPerfStats.pClCurrentPoint = pClStaticPoint;
 2793 
 2794    pClStaticPoint->blockEntry();
 2795 }
 2796 
 2797 DynamicPerformancePoint::~DynamicPerformancePoint( )
 2798 {
 2799    num iElapsed = getPerfCnt() - nClEntryTickCount;
 2800 
 2801    pClStaticPoint->blockExit((int)iElapsed);
 2802 
 2803    // up one level
 2804    if (pClStaticParent)
 2805       pClStaticParent->iClSubTimes += iElapsed;
 2806    glblPerfStats.pClCurrentPoint = pClStaticParent;
 2807 }
 2808 
 2809 void logProfile( )
 2810 {
 2811    num nTicksPerMSec = getPerfFreq() / 1000;
 2812    if (!nTicksPerMSec)
 2813        nTicksPerMSec = 1;
 2814 
 2815    printf("performance (%d points):\n", glblPerfStats.numberOfPoints());
 2816 
 2817    int iTotalMSec = 0;
 2818    int iTotalPerc = 0;
 2819 
 2820    for (int ipass=0; ipass<2; ipass++)
 2821    for (int i=0; i<glblPerfStats.numberOfPoints(); i++)
 2822    {
 2823       StaticPerformancePoint *p = glblPerfStats.getPoint(i);
 2824 
 2825       num nNetto = p->iClTotalTime - p->iClSubTimes;
 2826       num nMSec  = nNetto / nTicksPerMSec;
 2827 
 2828       if (!ipass) {
 2829          iTotalMSec += (int)nMSec;
 2830       } else {
 2831          int iPerc = nMSec * 100 / (iTotalMSec ? iTotalMSec : 1);
 2832          iTotalPerc += iPerc;
 2833          printf("% 10s : % 10I64d % 5d % 3d%% % 8I64d   %s:%d   (%d-%d)\n",
 2834             p->pszClID,
 2835             nNetto, (int)nMSec, iPerc,
 2836             p->iClHits,
 2837             p->pszClFile,
 2838             p->iClTraceLine,
 2839             (int)p->iClTotalTime, (int)p->iClSubTimes
 2840             );
 2841       }
 2842    }
 2843 
 2844       printf("% 10s : % 10I64d % 5d % 3d%% % 8I64d   %s:%d   (%d-%d)\n",
 2845          "total",
 2846          (num)0, iTotalMSec, iTotalPerc,
 2847          (num)0,
 2848          "any",
 2849          0,
 2850          0, 0
 2851          );
 2852 }
 2853 
 2854 #else
 2855 
 2856 void logProfile( )
 2857 {
 2858 }
 2859 
 2860 #endif
 2861 
 2862 // ====== SFK crash handler begin ========
 2863 
 2864 struct SFKSysLog
 2865 {
 2866    SFKSysLog ( );
 2867 
 2868    uint aSysLog[256];
 2869    unsigned char iSysLog;
 2870 };
 2871 
 2872 char *pszGlblSysLogFileName = 0;
 2873 
 2874 SFKSysLog::SFKSysLog( ) { memset(this, 0, sizeof(*this)); }
 2875 
 2876 struct SFKSysLog glblDefaultSysLog, glblDummySysLog;
 2877 
 2878 struct SFKSysLog *pGlblSysLog = &glblDefaultSysLog;
 2879 
 2880 #define MyModuleId 0 // not yet used
 2881 
 2882 #ifndef MTK_TRACE
 2883  #ifndef USE_SFK_BASE
 2884   #undef __
 2885   #define __                 \
 2886      pGlblSysLog->aSysLog[pGlblSysLog->iSysLog++] = \
 2887         (__LINE__     << 16) \
 2888       | (MyModuleId & 0xFFFFU);
 2889  #endif
 2890 #endif
 2891 
 2892 static void showSysLog( )
 2893 {
 2894    static char szBuf[2048];
 2895    static char szWord[100];
 2896    static const char *ahex = "0123456789ABCDEF";
 2897 
 2898    struct SFKSysLog *pCurLog = pGlblSysLog;
 2899 
 2900    // disable further writes to main log
 2901    pGlblSysLog = &glblDummySysLog;
 2902 
 2903    int istart=0,icur=0,isteps=0;
 2904    uint imix,iline=0,ithread=0;
 2905    char *pdst=0,*pmax=szBuf+sizeof(szBuf)-100;
 2906 
 2907    istart = pCurLog->iSysLog;
 2908    icur   = istart;
 2909    if (icur > 0) icur--; else icur = 0xFFU;
 2910 
 2911    pdst = szBuf;
 2912 
 2913    strcat(pdst, "program version: SFK " SFK_VERSION SFK_FIXPACK "\n"
 2914                 "\n=== last steps: ===\n");
 2915    pdst += strlen(pdst);
 2916 
 2917    for (; isteps < 100 && pdst < pmax; isteps++)
 2918    {
 2919       imix = pCurLog->aSysLog[icur];
 2920       iline = (imix >> 16) & 0xFFFFU;
 2921       ithread = (imix & 0xFFFFU);
 2922 
 2923       // 01.00000
 2924       // 012345678
 2925       pdst[0+1]  = ahex[(ithread >>  0) & 0x0F];
 2926       pdst[0+0]  = ahex[(ithread >>  4) & 0x0F];
 2927       pdst[0+2]  = '.';
 2928       pdst[3+4]  = ahex[(iline /     1) % 10];
 2929       pdst[3+3]  = ahex[(iline /    10) % 10];
 2930       pdst[3+2]  = ahex[(iline /   100) % 10];
 2931       pdst[3+1]  = ahex[(iline /  1000) % 10];
 2932       pdst[3+0]  = ahex[(iline / 10000) % 10];
 2933       pdst[8]    = ((isteps % 5)==4) ? '\n' : ' ';
 2934       pdst += 9;
 2935 
 2936       if (icur > 0) icur--; else icur = 0xFFU;
 2937    }
 2938 
 2939    *pdst = '\0';
 2940 
 2941    if (pszGlblSysLogFileName)
 2942    {
 2943       strcat(pdst, "=== see also: ");
 2944       strcat(pdst, pszGlblSysLogFileName);
 2945       strcat(pdst, " ===\n");
 2946  
 2947       // try to write a report file
 2948       FILE *fout = fopen(pszGlblSysLogFileName,"w");
 2949       if (fout)
 2950       {
 2951          fwrite(szBuf, 1, strlen(szBuf), fout);
 2952          fclose(fout);
 2953       }
 2954    }
 2955  
 2956    #ifdef _WIN32
 2957 
 2958    MessageBox(0, szBuf, "fatal error", MB_OK);
 2959 
 2960    #else
 2961 
 2962    fprintf(stderr, "=== fatal error during execution ===\n%s\n", szBuf);
 2963 
 2964    #endif
 2965 
 2966    exit(-1);
 2967 }
 2968 
 2969 // - - - - -
 2970 
 2971 static void crashTest( )
 2972 {
 2973    char *p = 0;
 2974    *p = 'a';
 2975 }
 2976 
 2977 static void traceModeCrashHandler(int sig)
 2978 {
 2979    static int crashRecursionCounter = 0;
 2980    crashRecursionCounter++;
 2981    if (crashRecursionCounter < 2)
 2982    {
 2983       crashRecursionCounter++;
 2984 
 2985       #ifdef WITH_TRACING
 2986       // dump stack trace to error log
 2987       mtkDumpLastSteps(0);
 2988       mtkDumpStackTrace(0);
 2989       #else
 2990       showSysLog();
 2991       #endif
 2992    }
 2993    printf("sfk exits due to segment violation.\n");
 2994    exit(255);
 2995 }
 2996 
 2997 #ifndef USE_SFK_BASE
 2998 void initCrashHandler(char *pszDumpFile)
 2999 {
 3000    pszGlblSysLogFileName = pszDumpFile; // if any
 3001 
 3002    void (*pfhand)(int) = traceModeCrashHandler;
 3003    sigset_t mask;
 3004    sigemptyset(&mask);
 3005 #ifdef SIGSEGV
 3006    signal (SIGSEGV, pfhand);
 3007    sigaddset(&mask, SIGSEGV);
 3008 #else
 3009    #error no_sigsegv_defined
 3010 #endif
 3011 #ifdef SIGFPE
 3012    signal (SIGFPE, pfhand);
 3013    sigaddset(&mask, SIGFPE);
 3014 #else
 3015    #error no_sigfpe_defined
 3016 #endif
 3017 #ifdef SIGILL
 3018    signal (SIGILL, pfhand);
 3019    sigaddset(&mask, SIGILL);
 3020 #else
 3021    #error no_sigill_defined
 3022 #endif
 3023 #ifdef SIGABRT
 3024    signal (SIGABRT, pfhand);
 3025    sigaddset(&mask, SIGABRT);
 3026 #else
 3027    #error no_sigabrt_defined
 3028 #endif
 3029    sigprocmask(SIG_UNBLOCK, &mask, 0);
 3030 }
 3031 #endif // USE_SFK_BASE
 3032 
 3033 // ====== SFK trace mode crash handler end ========
 3034 
 3035 int iGlblInScript = 0;
 3036 
 3037 // see also cs.curcmd
 3038 static bool bGlblCurCmdSet = 0;
 3039 static char szGlblCurCmd[50];
 3040 
 3041 class CommandScope {
 3042 public:
 3043       CommandScope (cchar *pszcmd);
 3044      ~CommandScope ( );
 3045 };
 3046 
 3047 CommandScope::CommandScope(cchar *pszcmd) {
 3048    strcopy(szGlblCurCmd, pszcmd);
 3049    bGlblCurCmdSet = 1;
 3050 }
 3051 
 3052 CommandScope::~CommandScope() {
 3053    bGlblCurCmdSet = 0;
 3054 }
 3055 
 3056 #ifdef VFILEBASE
 3057 
 3058 const char *pGlblHttpUserAgent = 0;
 3059 
 3060 static char szGlblUserAgent[100];
 3061 
 3062 void setHTTPUserAgent(const char *psz)
 3063    { pGlblHttpUserAgent = psz; }
 3064 
 3065 char *getHTTPUserAgent()
 3066 {
 3067    if (pGlblHttpUserAgent)
 3068       return str(pGlblHttpUserAgent); // sfk1933
 3069 
 3070    if (bGlblCurCmdSet)
 3071       snprintf(szGlblUserAgent, sizeof(szGlblUserAgent)-10,
 3072          "Swiss File Knife/" SFK_VERSION " (sfk %s, " VER_STR_OS ")",
 3073             szGlblCurCmd);
 3074    else
 3075       snprintf(szGlblUserAgent, sizeof(szGlblUserAgent)-10,
 3076          "Swiss File Knife/" SFK_VERSION " (" VER_STR_OS ")");
 3077 
 3078    char *psz = strstr(szGlblUserAgent, "windows-any)");
 3079    if (psz) strcpy(psz, "windows)");
 3080 
 3081    return szGlblUserAgent;
 3082 }
 3083 
 3084 #endif // VFILEBASE
 3085 
 3086 // fwrite on windows network drives may fail with blocks > 60 MB,
 3087 // therefore a single block write is limited to this size:
 3088 #define SFK_IO_BLOCK_SIZE 10000000 // about 10 MB
 3089 
 3090 // optional callback tracing for hot spots
 3091 void (*pGlblTraceCallback)(char *pmsg) = 0;
 3092 static char szTraceBuf[1024+10];
 3093 void cbtrace(const char *pszFormat, ...)
 3094 {
 3095    if (!pGlblTraceCallback) return;
 3096    va_list argList;
 3097    va_start(argList, pszFormat);
 3098    ::vsnprintf(szTraceBuf, sizeof(szTraceBuf)-10, pszFormat, argList);
 3099    szTraceBuf[sizeof(szTraceBuf)-10] = '\0';
 3100    pGlblTraceCallback(szTraceBuf);
 3101 }
 3102 
 3103 int getFileStat( // RC == 0 if exists anything
 3104    char  *pszName,
 3105    int   &rbIsDirectory,
 3106    int   &rbCanRead,
 3107    int   &rbCanWrite,
 3108    num   &rlFileTime,
 3109    num   &rlFileSize,
 3110    num   *ppcatimes  = 0,  // optional: creation and access time
 3111    void  *prawstat   = 0,  // optional: create copy of stat structure
 3112    int    nrawstatmax= 0,  // size of above buffer
 3113    uint   nmodeflags = 0   // bit 0: use alternative stat, if available
 3114    );
 3115 
 3116 #define delstring(x) \
 3117    if (x) {          \
 3118       delete [] x;   \
 3119       x = 0;         \
 3120    }
 3121 
 3122 // this also sets zero terminator at end of string,
 3123 // i.e. nmaxlen means including zero terminator.
 3124 void setattr(char *pdst, uchar uc, uint nlen, uint nmaxlen)
 3125 {
 3126    if (nlen > nmaxlen) nlen = nmaxlen;
 3127    if (nlen > 0) {
 3128       memset(pdst, uc, nlen-1);
 3129       pdst[nlen-1] = '\0';
 3130    } else {
 3131       pdst[0] = '\0';
 3132    }
 3133 }
 3134 
 3135 bool myisxdigit(char c) {
 3136    if (c >= '0' && c <= '9') return 1;
 3137    if (c >= 'a' && c <= 'f') return 1;
 3138    if (c >= 'A' && c <= 'F') return 1;
 3139    return 0;
 3140 }
 3141 
 3142 int getTwoDigitHex(char *psz)
 3143 {
 3144    char szHex[10];
 3145 
 3146    if (!*psz) return -1;
 3147    szHex[0] = tolower(*psz++);
 3148    if (!myisxdigit(szHex[0])) return -1;
 3149 
 3150    if (!*psz) return -1;
 3151    szHex[1] = tolower(*psz++);
 3152    if (!myisxdigit(szHex[1])) return -1;
 3153 
 3154    szHex[2] = '\0';
 3155 
 3156    return (int)strtoul(szHex,0,0x10);
 3157 }
 3158 
 3159 int getThreeDigitDec(char *psz)
 3160 {
 3161    char szDec[10];
 3162 
 3163    if (!*psz) return -1;
 3164    szDec[0] = tolower(*psz++);
 3165    if (!isdigit(szDec[0])) return -1;
 3166 
 3167    if (!*psz) return -1;
 3168    szDec[1] = tolower(*psz++);
 3169    if (!isdigit(szDec[1])) return -1;
 3170 
 3171    if (!*psz) return -1;
 3172    szDec[2] = tolower(*psz++);
 3173    if (!isdigit(szDec[2])) return -1;
 3174 
 3175    szDec[3] = '\0';
 3176 
 3177    return (int)strtoul(szDec,0,10);
 3178 }
 3179 
 3180 void doSleep(int nmsec)
 3181 {
 3182    #ifdef _WIN32
 3183    Sleep(nmsec);
 3184    #else
 3185    // sleep(1);
 3186    const timespec ts = { nmsec / 1000, nmsec % 1000 * 1000000 };
 3187    nanosleep(&ts, NULL);
 3188    #endif
 3189 }
 3190 
 3191 int nGlblShellRC = 0;
 3192 
 3193 struct CommandStats gs;    // global settings accross whole chain
 3194 struct CommandStats cs;    // command local statistics or settings
 3195 struct CommandStats cspre; // of previous command
 3196 struct CommandStats dummyCommandStats;
 3197 
 3198 num getTotalBytes()    { return cs.totalbytes; }
 3199 void clearTotalBytes() { cs.totalbytes=0; }
 3200 
 3201 #ifdef WINFULL
 3202 
 3203 #define wstrlen  wcslen    // use mystrwlen
 3204 #define wstrchr  wcschr
 3205 #define wstrrchr wcsrchr
 3206 
 3207 bool vname() { return cs.uname || cs.tname || cs.aname; }
 3208 
 3209 sfkname::sfkname(const char *psz, bool bpure)
 3210 {
 3211    memset(this, 0, sizeof(*this));
 3212    strcopy(szname, psz);
 3213    nstate = 1;
 3214 }
 3215 
 3216 sfkname::sfkname(ushort *pwsz)
 3217 {
 3218    memset(this, 0, sizeof(*this));
 3219    wcsncpy((wchar_t*)awname, (wchar_t*)pwsz, sfkmaxname);
 3220    nstate = 2;
 3221 }
 3222 
 3223 ushort *sfkname::wname( )
 3224 {
 3225    bbadconv = 0;
 3226 
 3227    if (nstate & 2)
 3228       return awname;
 3229    if (!(nstate & 1))
 3230       return 0;
 3231 
 3232    awname[0] = 0;
 3233 
 3234    // convert from szname
 3235    #ifdef SFK_UNAME
 3236    if (cs.uname)
 3237    {
 3238       mclear(awname);
 3239       int irc = MultiByteToWideChar(   // win.wname
 3240          CP_UTF8, 0,
 3241          szname, strlen(szname),
 3242          (wchar_t*)awname, sfkmaxname
 3243          );
 3244       if (!irc) bbadconv = 1;
 3245    }
 3246    else if (cs.aname)
 3247    {
 3248       mclear(awname);
 3249       int irc = MultiByteToWideChar(   // win.aname
 3250          CP_ACP, 0,
 3251          szname, strlen(szname),
 3252          (wchar_t*)awname, sfkmaxname
 3253          );
 3254       if (!irc) bbadconv = 1;
 3255    }
 3256    #endif // SFK_UNAME
 3257 
 3258    if (cs.tname) // tname.decode
 3259    {
 3260       ushort *pdstcur = awname;
 3261       ushort *pdstmax = pdstcur+sfkmaxname;
 3262       int i=0;
 3263       for (; pdstcur<pdstmax && szname[i]!=0; i++)
 3264       {
 3265          if (   szname[i+0]=='<'
 3266              && isxdigit(szname[i+1])
 3267              && isxdigit(szname[i+2])
 3268              && isxdigit(szname[i+3])
 3269              && isxdigit(szname[i+4])
 3270              && szname[i+5]=='>'
 3271             )
 3272          {
 3273             ushort c = (ushort)strtoul(szname+i+1,0,0x10);
 3274             *pdstcur++ = c;
 3275             i += 5;
 3276             continue;
 3277          }
 3278          *pdstcur++ = ((ushort)szname[i]) & 0xFFU;
 3279       }
 3280       *pdstcur = 0;
 3281    }
 3282 
 3283    return awname;
 3284 }
 3285 
 3286 char *sfkname::vname( )
 3287 {
 3288    bbadconv = 0;
 3289 
 3290    if (nstate & 1)
 3291       return szname;
 3292    if (!(nstate & 2))
 3293       return 0;
 3294 
 3295    szname[0] = '\0';
 3296 
 3297    // convert from wname
 3298    #ifdef SFK_UNAME
 3299    if (cs.uname)
 3300    {
 3301       // result is NOT zero terminated!
 3302       mclear(szname);
 3303       int irc = WideCharToMultiByte(   // vname.utf8
 3304          CP_UTF8, 0,
 3305          (wchar_t*)awname, wcslen((wchar_t*)awname),
 3306          szname, sfkmaxname, 0, 0);
 3307       if (!irc) bbadconv = 1;
 3308    }
 3309    else if (cs.aname)
 3310    {
 3311       int irc = sfkchars.strunitoansi(awname, sfkchars.wlen(awname),
 3312          szname, sfkmaxname);
 3313       if (irc > 0) bbadconv = 1;
 3314    }
 3315    #endif // SFK_UNAME
 3316 
 3317    if (cs.tname) // tname.encode
 3318    {
 3319       char *pdstcur = szname;
 3320       char *pdstmax = szname+sfkmaxname;
 3321       int i=0;
 3322       for (; pdstcur<pdstmax && awname[i]!=0; i++)
 3323       {
 3324          ushort c = awname[i];
 3325          if (c < 0x0100U) {
 3326             *pdstcur++ = (char)c;
 3327             continue;
 3328          }
 3329          sprintf(pdstcur, "<%04X>", c);
 3330          pdstcur += 6;
 3331       }
 3332       *pdstcur = '\0';
 3333    }
 3334 
 3335    return szname;
 3336 }
 3337 
 3338 #endif // WINFULL
 3339 
 3340 bool infoAllowed() {
 3341    if (cs.quiet)  return 0;
 3342    if (cs.noinfo) return 0;
 3343    return 1;
 3344 }
 3345 
 3346 int quietMode() { return cs.quiet; }
 3347 int fastMode()  { return cs.fast;  }
 3348 
 3349 #ifdef VFILEBASE
 3350 void setxelike(bool byes) { gs.xelike = cs.xelike = byes; }
 3351 #endif // VFILEBASE
 3352 
 3353 #if (defined(WITH_TCP) || defined(VFILENET) || defined(DV_TCP))
 3354 
 3355 // IN: flags bit 0: include port
 3356 //           bit 1: triple digits
 3357 char *ipAsString(struct sockaddr_in *pAddr, char *pszBuffer, int iBufferSize, uint uiFlags=0)
 3358 {
 3359    if (iBufferSize > 0)
 3360       pszBuffer[0] = '\0'; // safety
 3361  
 3362    if (iBufferSize < 20)
 3363       return str("?");
 3364 
 3365    uint uiIP  = pAddr->sin_addr.s_addr;
 3366    int  iPort = ntohs(pAddr->sin_port);
 3367 
 3368    char szPortInfo[50];
 3369    if (uiFlags & 1)
 3370       sprintf(szPortInfo, ":%d", iPort);
 3371    else
 3372       szPortInfo[0] = '\0';
 3373  
 3374    snprintf(pszBuffer, iBufferSize-4,
 3375       (uiFlags & 2) ? "%03u.%03u.%03u.%03u%s" : "%u.%u.%u.%u%s",
 3376       (uiIP >>  0) & 0xFFU,
 3377       (uiIP >>  8) & 0xFFU,
 3378       (uiIP >> 16) & 0xFFU,
 3379       (uiIP >> 24) & 0xFFU,
 3380       szPortInfo
 3381       );
 3382  
 3383    return pszBuffer;
 3384 }
 3385 
 3386 // mode 0: full list, or not, depending on environment
 3387 // mode 1: just a single ip
 3388 // mode 2: always full list
 3389 char *ownIPList(int &rhowmany, uint nPort, const char *psep, int nmode)
 3390 {
 3391    static char szIPListBuf[200];
 3392    szIPListBuf[0] = '\0';
 3393 
 3394    char szEnvNet[100]; szEnvNet[0]='\0';
 3395 
 3396    // if option -ownip=x is given
 3397    if (cs.szownip[0]) {
 3398       strcopy(szIPListBuf, cs.szownip);
 3399       return szIPListBuf;
 3400    }
 3401    if (nmode < 2)
 3402    {
 3403       char *pszEnvIP = getenv("SFK_OWN_IP"); // sfk1962
 3404       if (pszEnvIP) {
 3405          strcopy(szIPListBuf, pszEnvIP);
 3406          rhowmany = 1;
 3407          return szIPListBuf;
 3408       }
 3409       char *pszEnvNet = getenv("SFK_OWN_NET"); // sfk1962
 3410       if (pszEnvNet!=0 && strlen(pszEnvNet)>0) {
 3411          strcopy(szEnvNet, pszEnvNet);
 3412          if (szEnvNet[strlen(szEnvNet)-1]!='.')
 3413             strcat(szEnvNet, ".");
 3414       }
 3415    }
 3416 
 3417    prepareTCP();
 3418 
 3419    char szPortStr[50];
 3420    szPortStr[0] = '\0';
 3421    if (nPort > 0)
 3422       sprintf(szPortStr, ":%u", nPort);
 3423 
 3424    char *psz = 0;
 3425    int ndone = 0;
 3426    const char *pprefix = "";
 3427 
 3428    #ifdef _WIN32
 3429 
 3430    struct in_addr addr;
 3431 
 3432    hostent *pinfo = gethostbyname(""); // fails under linux
 3433    if (pinfo) 
 3434    {
 3435       for (int i=0; pinfo->h_addr_list[i]; i++)
 3436       {
 3437          memcpy(&addr,pinfo->h_addr_list[i],sizeof(struct in_addr));
 3438 
 3439          const char *pszIP = inet_ntoa(addr);
 3440 
 3441          if (strcmp(pszIP, "127.0.0.1"))
 3442          {
 3443             // sfk1962: filter by sfk_own_net if not forced list
 3444             if (nmode<2 && szEnvNet[0]!=0 && strncmp(pszIP,szEnvNet,strlen(szEnvNet))!=0)
 3445                continue;
 3446 
 3447             int nlen = strlen(pszIP);
 3448             int nrem = (int)sizeof(szIPListBuf) - (int)strlen(szIPListBuf);
 3449             if (nlen < nrem - 10) {
 3450                 strcat(szIPListBuf, pprefix);
 3451                 strcat(szIPListBuf, pszIP);
 3452                 if (nmode==1)
 3453                   return szIPListBuf;
 3454                 strcat(szIPListBuf, szPortStr); // if any
 3455                 pprefix = psep;
 3456                 ndone++;
 3457             }
 3458          }
 3459       }
 3460    }
 3461    rhowmany = ndone;
 3462 
 3463    #else
 3464 
 3465    // linux: list all existing interface IPV4 addresses
 3466    struct ifaddrs *pAdrObj = NULL;
 3467    char szAdrBuf[200]; mclear(szAdrBuf);
 3468    getifaddrs(&pAdrObj);
 3469    while (pAdrObj != NULL)
 3470    {
 3471      if (   pAdrObj->ifa_addr != 0
 3472          && pAdrObj->ifa_addr->sa_family == AF_INET
 3473          && pAdrObj->ifa_name != 0
 3474          && strcmp(pAdrObj->ifa_name, "lo0")
 3475         )
 3476      {
 3477        void *pIPData = &((struct sockaddr_in *)pAdrObj->ifa_addr)->sin_addr;
 3478        const char *pszIP = inet_ntop(AF_INET, pIPData, szAdrBuf, sizeof(szAdrBuf)-10);
 3479        if (strcmp(pszIP, "127.0.0.1"))
 3480        do
 3481        {
 3482           // sfk1962: filter by sfk_own_net if not forced list
 3483           if (nmode<2 && szEnvNet[0]!=0 && strncmp(pszIP,szEnvNet,strlen(szEnvNet))!=0)
 3484              break;
 3485 
 3486           int nlen = strlen(pszIP);
 3487           int nrem = (int)sizeof(szIPListBuf) - (int)strlen(szIPListBuf);
 3488           if (nlen < nrem - 10) {
 3489              strcat(szIPListBuf, pprefix);
 3490              strcat(szIPListBuf, pszIP);
 3491              if (nmode==1)
 3492                return szIPListBuf;
 3493              strcat(szIPListBuf, szPortStr); // if any
 3494              pprefix = psep;
 3495              ndone++;
 3496           }
 3497        }
 3498        while (0);
 3499      }
 3500      pAdrObj = pAdrObj->ifa_next;
 3501    }
 3502    rhowmany = ndone;
 3503 
 3504    #endif
 3505  
 3506    return szIPListBuf;
 3507 }
 3508 
 3509 // linux only
 3510 char *getMacForIP(uint uiIP)
 3511 {
 3512    #ifdef SFK_NATIVE_LINUX
 3513 
 3514    static char szBuf[100];
 3515    static int  iBuf = 0;
 3516    static int  iCtlSocket = INVALID_SOCKET;
 3517 
 3518     struct arpreq       areq;
 3519     struct sockaddr_in *sin;
 3520     struct in_addr      ipaddr;
 3521 
 3522    // get internet domain socket
 3523    if (iCtlSocket == INVALID_SOCKET)
 3524    {
 3525       int s;
 3526         if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
 3527           return 0;
 3528       iCtlSocket = s;
 3529    }
 3530    // it stays allocated for process lifetime.
 3531 
 3532    // make arp request  
 3533     memset(&areq, 0, sizeof(areq));
 3534     sin = (struct sockaddr_in *) &areq.arp_pa;
 3535     sin->sin_family = AF_INET;
 3536 
 3537    ipaddr.s_addr = uiIP;
 3538 
 3539     sin->sin_addr = ipaddr;
 3540     sin = (struct sockaddr_in *) &areq.arp_ha;
 3541     sin->sin_family = ARPHRD_ETHER;
 3542     
 3543     strncpy(areq.arp_dev, "eth0", 15);
 3544     
 3545     if (ioctl(iCtlSocket, SIOCGARP, (caddr_t) &areq) == -1)
 3546       return 0;
 3547 
 3548    sprintf(szBuf, "%02x:%02x:%02x:%02x:%02x:%02x",
 3549       areq.arp_ha.sa_data[0] & 0xFF,
 3550       areq.arp_ha.sa_data[1] & 0xFF,
 3551       areq.arp_ha.sa_data[2] & 0xFF,
 3552       areq.arp_ha.sa_data[3] & 0xFF,
 3553       areq.arp_ha.sa_data[4] & 0xFF,
 3554       areq.arp_ha.sa_data[5] & 0xFF
 3555       );
 3556 
 3557    return szBuf;
 3558 
 3559    #else
 3560 
 3561    return 0;
 3562 
 3563    #endif
 3564 }
 3565 
 3566 #endif // WITH_TCP or VFILENET
 3567 
 3568 void setArcTravel(bool bYesNo, bool bPreCache, int iProbeFiles)
 3569 {
 3570    gs.travelzips  = bYesNo;
 3571    cs.travelzips  = bYesNo;
 3572    if (iProbeFiles != 2)
 3573    {
 3574       gs.probefiles  = iProbeFiles;
 3575       cs.probefiles  = iProbeFiles;
 3576    }
 3577    #ifdef VFILEBASE
 3578    cs.precachezip = bPreCache;
 3579    #endif // VFILEBASE
 3580 }
 3581 
 3582 bool getArcTravel( ) { return cs.travelzips; }
 3583 
 3584 // for dview
 3585 void setLoadOffice(int iYesNo) {
 3586    gs.office = iYesNo;
 3587    cs.office = iYesNo;
 3588    if (!iYesNo) {
 3589       gs.justoffice = 0;
 3590       cs.justoffice = 0;
 3591    }
 3592 }
 3593 
 3594 // for dview
 3595 void setLoadJustOffice(int iYesNo) {
 3596    gs.justoffice = iYesNo;
 3597    cs.justoffice = iYesNo;
 3598 }
 3599 
 3600 void setSubLoad(bool bYesNo) {
 3601    mtklog(("setsubload %d", bYesNo));
 3602    gs.subdirs = bYesNo;
 3603    cs.subdirs = bYesNo;
 3604 }
 3605 
 3606 void setHiddenLoad(bool bYesNo) {
 3607    mtklog(("sethidload %d", bYesNo));
 3608    gs.hidden = bYesNo;
 3609    cs.hidden = bYesNo;
 3610 }
 3611 
 3612 void setBinaryLoad(bool bYesNo) {
 3613    mtklog(("setbinload %d", bYesNo));
 3614    gs.incbin = bYesNo;
 3615    cs.incbin = bYesNo;
 3616 }
 3617 
 3618 void setUTFLoad(bool bYesNo) {
 3619    mtklog(("setutfload %d", bYesNo));
 3620    gs.wchardec = bYesNo;
 3621    cs.wchardec = bYesNo;
 3622 }
 3623 
 3624 int getWrapLoad(bool &rrewrap) {
 3625    rrewrap = cs.rewrap;
 3626    return cs.wrapcol;
 3627 }
 3628 void setWrapLoad(int n, bool brewrap) {
 3629    cs.wrapcol = n;
 3630    gs.wrapcol = n;
 3631    cs.wrapbincol = (n >= 80) ? ((n * 90) / 100 - 10) : 80;
 3632    gs.wrapbincol = cs.wrapbincol;
 3633    cs.rewrap = brewrap;
 3634    gs.rewrap = brewrap;
 3635 }
 3636 
 3637 // tells if cur matches mask, including variations:
 3638 // if mask="-sep" then "-sep" and "-ssep" is allowed.
 3639 bool isxopt(char *pszcur, cchar *pszmask)
 3640 {
 3641    char szBuf[100];
 3642    if (pszmask[0] != '-') return 0;
 3643    if (strlen(pszmask) > sizeof(szBuf)-10) return 0;
 3644 
 3645    // check for indirect hit: "-seps".
 3646    // if so, auto-activate slash patterns for this command.
 3647    sprintf(szBuf, "%ss", pszmask);
 3648    if (!strncmp(pszcur, szBuf, strlen(szBuf))) {
 3649       cs.spat = 1;
 3650       return 1;
 3651    }
 3652 
 3653    // check for direct hit: "-sep"
 3654    if (!strncmp(pszcur, pszmask, strlen(pszmask))) return 1;
 3655 
 3656    // check for indirect hit: "-ssep"
 3657    cchar *pszbase = pszmask+1;
 3658 
 3659    // if so, auto-activate slash patterns for this command.
 3660    sprintf(szBuf, "-s%s", pszbase);
 3661    if (!strncmp(pszcur, szBuf, strlen(szBuf))) {
 3662       cs.spat = 1;
 3663       return 1;
 3664    }
 3665 
 3666    // check for "-uform" to activate -upat syntax.
 3667    sprintf(szBuf, "-u%s", pszbase);
 3668    if (!strncmp(pszcur, szBuf, strlen(szBuf))) {
 3669       setLinuxSyntax(1); // -uform
 3670       return 1;
 3671    }
 3672 
 3673    return 0;
 3674 }
 3675 
 3676 // tells if cur matches mask, without variations.
 3677 bool isopt(char *pszcur, char *pszmask) {
 3678    if (!strncmp(pszcur, pszmask, strlen(pszmask))) return 1;
 3679    return 0;
 3680 }
 3681 
 3682 CommandStats::CommandStats()
 3683 {
 3684    memset(this, 0, sizeof(*this));
 3685    reset();
 3686 }
 3687 
 3688 void CommandStats::reset()
 3689 {
 3690    memset(this, 0, sizeof(*this));
 3691    wpat        =  1;
 3692    runCmd      = str("");
 3693    treeStopRC  = 19; // NOT 9
 3694    subdirs     =  1;
 3695    utf8dec     =  0;
 3696    wchardec    =  0; // experimental, NOT yet default
 3697    usecirclemap=  1;
 3698    wrapbincol  = 80; // default
 3699    addsnaplf   = "\n";
 3700    withrootdirs=  1;
 3701    #ifdef _WIN32
 3702    strcpy(szeol, "\r\n");
 3703    #else
 3704    strcpy(szeol, "\n");
 3705    #endif
 3706    toisodef    = '.';
 3707    maxwebsize  = 100 * 1000000; // sfk196
 3708 
 3709    // sfk180: label -qtrim is default.
 3710    mlquotes    = 'f';
 3711    cs.curcmd[0] = '\0';
 3712 
 3713    cs.cweb     = 1;
 3714 
 3715    #ifdef _WIN32
 3716    usecolor       =  1;
 3717    usehelpcolor   =  1;
 3718    #else
 3719    usecolor       =  0;
 3720    usehelpcolor   =  0;
 3721    #endif
 3722 
 3723    outcconv       = 1;  // sfkwin only
 3724    forcecconv     = 0;  // sfkwin only
 3725 }
 3726 
 3727 bool CommandStats::stopTree(int nrc, bool *psilent)
 3728 {
 3729    int lRC = 0;
 3730    int nShellRC = 0;
 3731    if (cs.stopfiletree) {
 3732       if (psilent)
 3733          *psilent = 1;
 3734       return 1;
 3735    }
 3736    if (nrc >= treeStopRC) {
 3737       if (!toldTreeStop) {
 3738          toldTreeStop = 1;
 3739          #ifndef USE_SFK_BASE
 3740          int pinf(const char *pszFormat, ...);
 3741          pinf("directory tree processing stopped by error.\n");
 3742          #endif // USE_SFK_BASE
 3743       }
 3744       lRC = 1;
 3745       // on stop, always map fatal rc to shell rc
 3746       if (nrc >= 9) nShellRC = 9;   // error occurred, processing stopped.
 3747       else          nShellRC = nrc; // should not happen
 3748    } else {
 3749       // map masked rc to shell rc
 3750       if (cs.rcFromError) {
 3751          if (nrc >= 7) nShellRC = 7;   // error occurred, processing continued.
 3752          else          nShellRC = nrc; // any other code below 7
 3753       }
 3754    }
 3755    // build maximum shell rc, if any
 3756    if (nShellRC > nGlblShellRC)
 3757       nGlblShellRC = nShellRC;
 3758    // continue or stop tree processing
 3759    return lRC ? 1 : 0;
 3760 }
 3761 
 3762 bool CommandStats::showstat( )
 3763 {
 3764    if (cs.nostat) return 0;
 3765    if (cs.dostat) return 1;
 3766    if (cs.quiet)  return 0;
 3767    return 1;
 3768 }
 3769 
 3770 struct CommandPermamentStorage
 3771 {
 3772 public:
 3773    CommandPermamentStorage ( );
 3774 
 3775    num  tailnsize;   // current file size of tail
 3776    num  tailnpos;    // current read position of tail
 3777    num  tailtime;    // current file modification time
 3778    bool keeptmp;     // do not autodelete tmp files
 3779    bool showtmp;     // verbosely list names of created tmpfiles
 3780 }
 3781    cperm;
 3782 
 3783 CommandPermamentStorage::CommandPermamentStorage() { memset(this, 0, sizeof(*this)); }
 3784 
 3785 #if (defined(WITH_TCP) || defined(VFILENET) || defined(DV_TCP))
 3786 
 3787 extern int netErrno();
 3788 extern char *netErrStr();
 3789 
 3790 // flags.0: silent.
 3791 // flags.1: tell expanded ip as rc 1.
 3792 // rc 0: ok done
 3793 // rc 1: ip expanded (only on flags.1)
 3794 // rc 9: error
 3795 int setaddr(struct sockaddr_in *paddr, char *pstr, int iflags)
 3796 {
 3797    bool bsilent  = (iflags & 1) ? 1 : 0;
 3798    bool btellexp = (iflags & 2) ? 1 : 0;
 3799    bool bdidexp  = 0;
 3800 
 3801    char szExpBuf[100];
 3802 
 3803    int  idig=0,idot=0,iother=0;
 3804    int  ilen=strlen(pstr);
 3805    int  istate=0,nseg=0;
 3806 
 3807    for (int i=0; i<ilen; i++)
 3808    {
 3809       char c = pstr[i];
 3810       if (isdigit(c)) {
 3811          idig++;
 3812          if (istate==0) {
 3813             istate=1;
 3814             nseg++;
 3815          }
 3816       }
 3817       else if (c == '.') {
 3818          idot++;
 3819          istate=0;
 3820       }
 3821       else {
 3822          iother++;
 3823       }
 3824    }
 3825 
 3826    if (!cs.noipexpand && iother==0 && nseg >= 1 && nseg <= 2)
 3827    {
 3828       //    22 -> 192.168.1.22
 3829       //   .22 -> 192.168.1.22
 3830       //  1.22 -> 192.168.1.22
 3831       // .1.22 -> 192.168.1.22
 3832       mclear(szExpBuf);
 3833 
 3834       int   nnum  = 0; // number of own ip's
 3835       char *plist = ownIPList(nnum, 0, "\t", 1); // setaddr
 3836 
 3837       // copy first 3 segments of own ip, if any
 3838       char *pseg2=0,*pseg3=0,*pseg4=0;
 3839       pseg2 = strchr(plist, '.');
 3840       if (pseg2) pseg3 = strchr(pseg2+1, '.');
 3841       if (pseg3) pseg4 = strchr(pseg3+1, '.');
 3842       if (pseg4)
 3843       {
 3844          char *pseg = (nseg == 2) ? pseg3 : pseg4;
 3845          int   nlen = (pseg - plist) + 1;
 3846          memcpy(szExpBuf, plist, nlen);
 3847          if (*pstr=='.')
 3848             pstr++;
 3849          strcpy(szExpBuf+nlen, pstr);
 3850          if (cs.showip)
 3851             pinf("[nopre] [using %s]\n", szExpBuf);
 3852          pstr = szExpBuf;
 3853          bdidexp = 1;
 3854       }
 3855    }
 3856 
 3857    if (iother==0)
 3858    {
 3859       // pure ip given
 3860       paddr->sin_addr.s_addr = inet_addr(pstr);
 3861 
 3862       if (cs.debug)
 3863          printf("using inetaddr %x\n", paddr->sin_addr.s_addr);
 3864    }
 3865    else
 3866    {
 3867       // must be a hostname
 3868       struct hostent *pTarget = gethostbyname(pstr);
 3869 
 3870       if (pTarget == NULL) {
 3871          if (!bsilent)
 3872             perr("cannot get hostname %s, rc=%d (%s)\n", pstr, netErrno(), netErrStr());
 3873          return 9;
 3874       }
 3875 
 3876       memcpy(&paddr->sin_addr.s_addr, pTarget->h_addr, pTarget->h_length);
 3877 
 3878       if (cs.debug)
 3879          printf("using gethost %x\n", paddr->sin_addr.s_addr);
 3880    }
 3881 
 3882    if (btellexp)
 3883       return bdidexp;
 3884 
 3885    return 0;
 3886 }
 3887 
 3888 // a hostbyname with automatic extension of "22" to "192.168.1.22"
 3889 struct hostent *sfkhostbyname(const char *pstr, bool bsilent)
 3890 {
 3891    char szExpBuf[100];
 3892    int  idig=0,idot=0,ioth=0;
 3893    int  ilen=strlen(pstr);
 3894    int  istate=0,nseg=0;
 3895 
 3896    for (int i=0; i<ilen; i++)
 3897    {
 3898       char c = pstr[i];
 3899       if (isdigit(c)) {
 3900          idig++;
 3901          if (istate==0) {
 3902             istate=1;
 3903             nseg++;
 3904          }
 3905       }
 3906       else if (c == '.') {
 3907          idot++;
 3908          istate=0;
 3909       }
 3910       else {
 3911          ioth++;
 3912       }
 3913    }
 3914 
 3915    if (!cs.noipexpand && ioth==0 && nseg >= 1 && nseg <= 2)
 3916    {
 3917       //    22 -> 192.168.1.22
 3918       //   .22 -> 192.168.1.22
 3919       //  1.22 -> 192.168.1.22
 3920       // .1.22 -> 192.168.1.22
 3921       mclear(szExpBuf);
 3922 
 3923       int   nnum  = 0; // number of own ip's
 3924       char *plist = ownIPList(nnum, 0, "\t", 1); // sfkhostbyname
 3925 
 3926       // copy first 3 segments of own ip, if any
 3927       char *pseg2=0,*pseg3=0,*pseg4=0;
 3928       pseg2 = strchr(plist, '.');
 3929       if (pseg2) pseg3 = strchr(pseg2+1, '.');
 3930       if (pseg3) pseg4 = strchr(pseg3+1, '.');
 3931       if (pseg4)
 3932       {
 3933          char *pseg = (nseg == 2) ? pseg3 : pseg4;
 3934          int   nlen = (pseg - plist) + 1;
 3935          memcpy(szExpBuf, plist, nlen);
 3936          if (*pstr=='.')
 3937             pstr++;
 3938          strcpy(szExpBuf+nlen, pstr);
 3939          if (cs.showip)
 3940             pinf("[nopre] [using %s]\n", szExpBuf);
 3941          pstr = szExpBuf;
 3942       }
 3943    }
 3944 
 3945    return gethostbyname(pstr);
 3946 }
 3947 
 3948 #endif // USE_TCP
 3949 
 3950 void getUpto(int iMaxCopy, char *pszSrc, int iMaxSrc, char *pszBuf, int iMaxBuf)
 3951 {
 3952    int i=0;
 3953    for (i=0; (i<iMaxCopy) && (i<iMaxBuf-1); i++)
 3954    {
 3955       if (iMaxSrc > 0) {
 3956          pszBuf[i] = pszSrc[i];
 3957          iMaxSrc--;
 3958       } else {
 3959          pszBuf[i] = ' ';
 3960       }
 3961    }
 3962    pszBuf[i] = '\0';
 3963 }
 3964 
 3965 int shrinkFormTextVar(char *psz, int &rLen, uchar **ppFlags)
 3966 {
 3967    if (!cs.spat)
 3968       return 0; // nothing to do
 3969 
 3970    bool bstrict = (cs.spat >= 2) ? 1 : 0;
 3971 
 3972    return shrinkFormTextBlock(psz, rLen, bstrict, cs.xchars, ppFlags);
 3973 }
 3974 
 3975 // replace \\t and \\xnn by a single character,
 3976 // within a binary block that is not zero-terminated.
 3977 int shrinkFormTextBlock(char *psz, int &rLen, bool bstrict, bool xchars, uchar **ppFlags)
 3978 {
 3979    char szBuf[20];
 3980 
 3981    int iCodePoints = 0;
 3982  
 3983    uchar uc = 0;
 3984    bool bAnyX = 0;
 3985    uchar *pFlags = 0;
 3986    uchar *pDst = 0;
 3987 
 3988    char *pszMax = psz + rLen;
 3989 
 3990    for (int ipass=0; ipass<2; ipass++)
 3991    {
 3992       char *pszCur = psz;
 3993 
 3994       if (ipass)
 3995       {
 3996          if (!xchars && bAnyX && ppFlags)
 3997          {
 3998             if (!(pFlags = newBitField(iCodePoints+4)))
 3999                return 9+perr("out of memory");
 4000             *ppFlags = pFlags;
 4001          }
 4002 
 4003          pDst = (uchar*)psz;
 4004       }
 4005 
 4006       iCodePoints = 0;
 4007 
 4008       while (pszCur < pszMax)
 4009       {
 4010          uc = *pszCur++;
 4011 
 4012          if (uc == '\\' && pszCur < pszMax)  // spat.1 shrinktext 2
 4013          {
 4014             switch (*pszCur++)
 4015             {
 4016                case '\\':  uc = '\\';  break;
 4017                case 't' :  uc = '\t';  break;
 4018                case 'q' :  uc = '"';   break;
 4019                case 'n' :  uc = '\n';  break;
 4020                case 'r' :  uc = '\r';  break;
 4021                case ' ' :  uc = ' ';   break;
 4022                case 'x' :
 4023                {
 4024                   if (pszCur+2 > pszMax) {
 4025                      getUpto(4, pszCur-2, 4, szBuf, sizeof(szBuf)-4);
 4026                      perr("incomplete hex slash pattern: %.4s   (use \\%.4s instead?)\n", szBuf, szBuf);
 4027                      return 9;
 4028                   }
 4029                   int n = getTwoDigitHex(pszCur);
 4030                   if (n < 0) {
 4031                      getUpto(4, pszCur-2, 4, szBuf, sizeof(szBuf)-4);
 4032                      perr("wrong hex slash pattern: %.4s   (use \\%.4s instead?)\n", szBuf, szBuf);
 4033                      return 9;
 4034                   }
 4035                   pszCur += 2;
 4036                   uc = (uchar)n;
 4037                   if (pFlags)
 4038                      sfkSetBit(pFlags, iCodePoints);
 4039                   bAnyX = 1;
 4040                   break;
 4041                }
 4042                case 'd' : // sfk 1723 added: "\d255"
 4043                if (bstrict)
 4044                {
 4045                   if (pszCur+3 > pszMax) {
 4046                      getUpto(5, pszCur-2, 5, szBuf, sizeof(szBuf)-4);
 4047                      perr("incomplete decimal slash pattern: %.4s   (use \\%.4s instead?)\n", szBuf, szBuf);
 4048                      return 9;
 4049                   }
 4050                   int n = getThreeDigitDec(pszCur);
 4051                   if (n < 0 || n > 255) {
 4052                      getUpto(5, pszCur-2, 5, szBuf, sizeof(szBuf)-4);
 4053                      perr("wrong decimal slash pattern: %.4s   (use \\%.4s instead?)\n", szBuf, szBuf);
 4054                      return 9;
 4055                   }
 4056                   pszCur += 3;
 4057                   uc = (uchar)n;
 4058                   if (pFlags)
 4059                      sfkSetBit(pFlags, iCodePoints);
 4060                   bAnyX = 1;
 4061                   break;
 4062                }
 4063                default:
 4064                   if (bstrict) {
 4065                      getUpto(2, pszCur-1, 2, szBuf, sizeof(szBuf)-4);
 4066                      perr("undefined slash pattern: %.2s   (use \\%.2s instead)\n", szBuf, szBuf);
 4067                      return 9;
 4068                   }
 4069             }  // endswitch
 4070          }  // endif
 4071 
 4072          if (ipass && pDst)
 4073             *pDst++ = uc;
 4074 
 4075          iCodePoints++;
 4076       }
 4077    }
 4078 
 4079    if (pDst < (uchar*)pszMax)
 4080       *pDst = '\0';
 4081 
 4082    rLen = (int)(pDst - (uchar*)psz);
 4083 
 4084    return 0;
 4085 }
 4086 
 4087 int copyFromFormText(char *pSrc, int iMaxSrc, char *pDstIn, int iMaxDst, uint nflags=0)
 4088 {
 4089    char szBuf[20];
 4090 
 4091    char *pszCur = pSrc;
 4092    char *pszMax = pSrc+iMaxSrc;
 4093 
 4094    char *pDst   = pDstIn;
 4095    char *pDstMax= pDstIn+iMaxDst;
 4096  
 4097    uchar uc;
 4098 
 4099    while (pszCur < pszMax && pDst+10 < pDstMax)
 4100    {
 4101       uc = *pszCur++;
 4102 
 4103       if (uc == '\\' && pszCur < pszMax)  // spat.2 copytext 2
 4104       {
 4105          // optionally escape phraser sequences
 4106          char c = *pszCur;
 4107          if ((nflags & 2) && (c==',' || c=='+' || c=='\t'))
 4108             { }
 4109          else
 4110          switch (*pszCur++)
 4111          {
 4112             case '\\':  uc = '\\';  break;
 4113             case 't' :  uc = '\t';  break;
 4114             case 'q' :  uc = '"';   break;
 4115             case 'n' :  uc = '\n';  break;
 4116             case 'r' :  uc = '\r';  break;
 4117             case ' ' :  uc = ' ';   break;
 4118             case 'x' :
 4119             {
 4120                if (pszCur+2 > pszMax) {
 4121                   getUpto(4, pszCur-2, 4, szBuf, sizeof(szBuf)-4);
 4122                   perr("incomplete hex slash pattern: %.4s   (use \\%.4s instead?)\n", szBuf, szBuf);
 4123                   return -1;
 4124                }
 4125                int n = getTwoDigitHex(pszCur);
 4126                if (n < 0) {
 4127                   getUpto(4, pszCur-2, 4, szBuf, sizeof(szBuf)-4);
 4128                   perr("wrong hex slash pattern: %.4s   (use \\%.4s instead?)\n", szBuf, szBuf);
 4129                   return -1;
 4130                }
 4131                pszCur += 2;
 4132                uc = (uchar)n;
 4133                break;
 4134             }
 4135             case 'd' :
 4136             {
 4137                if (pszCur+3 > pszMax) {
 4138                   getUpto(5, pszCur-2, 5, szBuf, sizeof(szBuf)-4);
 4139                   perr("incomplete dec slash pattern: %.5s   (use \\%.5s instead?)\n", szBuf, szBuf);
 4140                   return -1;
 4141                }
 4142                int n = getThreeDigitDec(pszCur);
 4143                if (n < 0) {
 4144                   getUpto(5, pszCur-2, 5, szBuf, sizeof(szBuf)-4);
 4145                   perr("wrong dec slash pattern: %.5s   (use \\%.5s instead?)\n", szBuf, szBuf);
 4146                   return -1;
 4147                }
 4148                pszCur += 3;
 4149                uc = (uchar)n;
 4150                break;
 4151             }
 4152             default:
 4153                getUpto(2, pszCur-1, 2, szBuf, sizeof(szBuf)-4);
 4154                perr("undefined slash pattern: %.2s   (use \\\\%.2s instead?)\n", szBuf, szBuf);
 4155                return -1;
 4156          }
 4157       }
 4158 
 4159       *pDst++ = uc;
 4160    }
 4161 
 4162    *pDst = '\0';
 4163 
 4164    return (int)(pDst-pDstIn);
 4165 }
 4166 
 4167 int nGlblFunc         = 0;
 4168 bool bGlblSyntaxTest  = 0;
 4169 int  bGlblCollectHelp = 0;
 4170 int  iGlblCollectCmd  = 0;
 4171 int nGlblActiveFileAgeLimit = 30; // days
 4172 int nGlblErrors   = 0; // perr counter
 4173 int nGlblWarnings = 0;
 4174 int nGlblTraceSel = 0; // b0:dirs b1:files
 4175 bool bGlblMD5RelNames = 0;
 4176 bool bGlblHaveInteractiveConsole = 0;
 4177 bool bGlblStartedInEmptyConsole = 0;
 4178 // bool bGlblEnableOPrintf = 1; // allow codepage conversion w/in oprintf
 4179 // bool bGlblForceCConv = 0;    // enfore codepage conversion w/in oprintf
 4180 bool bGlblAllowGeneralPure = 0; // command dependent
 4181 bool bGlblPauseOnError = 0;  // pause after every error
 4182 bool bGlblPauseOnEnd   = 0;  // pause before program end
 4183 bool bGlblOldMD5 = 0;
 4184 char *pszGlblOutFile = 0;  // if set, some funcs will take care not to read this file
 4185 char *pszGlblSaveTo  = 0;  // if set, some funcs will save output files to this path
 4186 FILE *fGlblOut     = 0; // general use
 4187 
 4188 #ifdef VFILEBASE
 4189    #include "sfkext.hpp"
 4190 #endif // VFILEBASE
 4191 
 4192 unsigned char abBuf[MAX_ABBUF_SIZE+100];
 4193 char szLineBuf[MAX_LINE_LEN+10];
 4194 char szLineBuf2[MAX_LINE_LEN+10];
 4195 char szLineBuf3[MAX_LINE_LEN+10];
 4196 char szAttrBuf[MAX_LINE_LEN+10];
 4197 char szAttrBuf2[MAX_LINE_LEN+10];
 4198 char szAttrBuf3[MAX_LINE_LEN+10];
 4199 char szRefNameBuf[MAX_LINE_LEN+10];
 4200 char szRefNameBuf2[MAX_LINE_LEN+10];
 4201 char szOutNameRecent[MAX_LINE_LEN+10];
 4202 char szTopURLBuf[MAX_LINE_LEN+10];
 4203 char szIOTraceBuf[200];
 4204 bool bGlblToldAboutRecent = 0;
 4205 char szOutNameBuf[MAX_LINE_LEN+10];
 4206 char szRunCmdBuf[MAX_LINE_LEN+10];
 4207 #define MAX_MATCH_BUF 500
 4208 char szMatchBuf[MAX_MATCH_BUF+10];  // strmatch word buffer
 4209 bool szMatchEsc[MAX_MATCH_BUF+10];  // strmatch escape flags
 4210 char *pszGlblPreRoot = 0;
 4211 char szGlblMixRoot[MAX_LINE_LEN+10];
 4212 char *pGlblDumpBuf = 0;
 4213 int   iGlblDumpBufSize = 0;
 4214 bool bErrBufSet = 0;
 4215 cchar *pszGlblJamPrefix = ":file:";
 4216 char *pszGlblJamRoot = 0;
 4217 bool  bGlblJamPure = 0;
 4218 #define MAX_JAM_TARGETS 1000
 4219 char *apJamTargets[MAX_JAM_TARGETS];
 4220 num   alJamTargetTime[MAX_JAM_TARGETS];
 4221 num   nJamSnapTime = -1;
 4222 int  nJamTargets  = 0;
 4223 char *pszGlblRepSrc = 0;
 4224 char *pszGlblRepDst = 0;
 4225 cchar *pszGlblBlank =
 4226    "                                                "
 4227    "                                                ";
 4228 num  nGlblStartTime = 0;
 4229 num  nGlblListMinSize = 0; // in bytes
 4230 int nGlblListMode = 0;    // 1==stat 2==list
 4231 int  nGlblListDigits = 12;
 4232 bool bGlblDisableEscape = 0;
 4233 bool bGlblEscape = 0;
 4234 bool bGlblEnter = 0;
 4235 char *pszGlblDstRoot = 0;
 4236 char *pszGlblDirTimes = 0;
 4237 cchar *pszGlblTurn = "\\|/-";
 4238 int  nGlblTurnCnt = 0;
 4239 bool  bGlblQuoted     = 0; // list: add quotes around filenames
 4240 bool  bGlblNoRootDirFiles = 0; // list -dir +dirmask
 4241 uint nGlblConvTarget = 0; // see eConvTargetFormats
 4242 uint aGlblConvStat[10];
 4243 #ifdef WITH_FN_INST
 4244 bool  bGlblInstRevoke = 0;
 4245 bool  bGlblInstRedo   = 0;
 4246 bool  bGlblInstEol    = 0;
 4247 cchar *pszGlblInstInc  = "";
 4248 cchar *pszGlblInstMac  = "";
 4249 static bool bGlblTouchOnRevoke = 1;
 4250 #endif
 4251 bool  bGlblRefRelCmp    = 1;
 4252 bool  bGlblRefBaseCmp   = 0;
 4253 bool  bGlblRefWideInfo  = 0;
 4254 int  nGlblRefMaxSrc    = 10;
 4255 bool  bGlblRefLimitReached = 0;
 4256 bool  bGlblStdInAny     = 0;  // all cmd except run: take list from stdin
 4257 bool  bGlblStdInFiles   = 0;  // run only: take filename list from stdin
 4258 bool  bGlblStdInDirs    = 0;  // run only: take directory list from stdin
 4259 int  nGlblMD5Skip      = 0;
 4260 bool  bGlblMirrorByDate = 0;  // inofficial, might be removed.
 4261 int nGlblTCPMaxSizeMB   = 500; // MB
 4262 SOCKET hGlblTCPOutSocket = 0;
 4263 bool bGlblFTPReadWrite   = 0;
 4264 bool bGlblFTPListFlatTS  = 0; // server: send flat timestamp on list
 4265 bool bGlblFTPListAsHTML  = 0;
 4266 bool bGlblFTPListTextBin = 0;
 4267 bool bGlblBinGrep           = 0;
 4268 bool bGlblBinGrepAutoDetect = 1;
 4269 int nGlblDarkColBase    = 0;
 4270 int nGlblBrightColBase  = 1;
 4271 bool bGlblSysErrDetail  = 0;
 4272 
 4273 // highlight=1 red=2 green=4 blue=8
 4274 #ifdef _WIN32
 4275 // windows default safety colors for ANY background.
 4276 // will be changed automatically if black background is detected.
 4277 // help part
 4278 int nGlblHeadColor      =  5; // green
 4279 int nGlblExampColor     = 11; // purple
 4280 // functional part
 4281 int nGlblFileColor      = 12; // cyan
 4282 int nGlblLinkColor      = 12; // cyan
 4283 int nGlblHitColor       =  5; // green
 4284 int nGlblRepColor       = 11; // purple
 4285 int nGlblErrColor       =  3; // red
 4286 int nGlblWarnColor      = 11; // purple
 4287 int nGlblPreColor       = 12; // cyan
 4288 int nGlblTimeColor      = 12; // cyan
 4289 int nGlblTraceIncColor  = 12; // cyan
 4290 int nGlblTraceExcColor  = 11; // purple
 4291 #else
 4292 // unix default colors for white background
 4293 int nGlblDefColor       =  0; // default
 4294 int nGlblHeadColor      =  4; // green
 4295 int nGlblExampColor     = 10; // purple
 4296 // functional part
 4297 int nGlblFileColor      = 10; // purple
 4298 int nGlblLinkColor      = 12; // cyan
 4299 int nGlblHitColor       =  4; // green
 4300 int nGlblRepColor       = 12; // blue
 4301 int nGlblErrColor       =  2; // red
 4302 int nGlblWarnColor      =  2; // red
 4303 int nGlblPreColor       = 12; // blue
 4304 int nGlblTimeColor      = 12; // blue
 4305 int nGlblTraceIncColor  = 12; // cyan
 4306 int nGlblTraceExcColor  = 11; // purple
 4307 #endif
 4308 
 4309 bool bGlblGrepLineNum    = 0;
 4310 bool bGlblHtml           = 0;  // for html help creation
 4311 // bool bGlblShortSyntax    = 0;
 4312 // bool bGlblAnyUsed        = 0;
 4313 bool bGlblAllowAllPlusPosFile = 0;
 4314 char *pszGlblSinceDir    = 0;
 4315 bool bGlblSinceDirIncRef = 0;
 4316 int nGlblMissingRefDirs  = 0;
 4317 int nGlblMatchingRefDirs = 0;
 4318 int nGlblSinceMode       = 0; // b0:add b1:dif
 4319 bool bGlblIgnoreTime     = 0;
 4320 bool bGlblIgnore3600     = 0;
 4321 char  *pszGlblCopySrc    = 0;
 4322 char  *pszGlblCopyDst    = 0;
 4323 uchar *pGlblWorkBuf      = 0;
 4324 num    nGlblWorkBufSize  = 0;
 4325 int   nGlblCopyStyle     = 2; // how filenames are dumped onto terminal
 4326 int   nGlblCopyShadows   = 0;
 4327 num   nGlblShadowSizeLimit = 0;
 4328 bool  bGlblUseCopyCache  = 0;
 4329 bool  bGlblShowSyncDiff  = 0;
 4330 bool  bGlblHavePlusDirMasks = 0; // deprecated
 4331 num   nGlblMemLimit      = 300 * 1048576;
 4332 bool  bGlblMemLimitWasSet = 0;
 4333 bool  bGlblNoMemCheck     = 0;
 4334 bool  bGlblSFKCreateFiles = 0;
 4335 char  *pGlblCurrentScript = 0; // while within a script command
 4336 char  *pGlblCurScriptName = 0; // and it's file name
 4337 
 4338 bool bGlblHexDumpWide    = 0;
 4339 int nGlblHexDumpForm     = 0;
 4340 num  nGlblHexDumpOff     = 0;
 4341 num  nGlblHexDumpLen     = 0;
 4342 
 4343 #ifdef _WIN32
 4344 cchar *pszGlblAliasBatchHead = "@rem sfk alias batch";
 4345 #else
 4346 cchar *pszGlblAliasBatchHead = "# sfk alias batch";
 4347 #endif
 4348 
 4349 int nGlblFzMisArcFiles = 0;
 4350 int nGlblFzConArcFiles = 0;
 4351 int nGlblFzConArchives = 0;
 4352 int nGlblFzMisCopFiles = 0;
 4353 int nGlblFzConCopFiles = 0;
 4354 
 4355 void cleanupTmpCmdData();
 4356 void shutdownAllGlobalData();
 4357 
 4358 // circular link processing blocker.
 4359 // used only in a vertical walkFiles() processing run,
 4360 // e.g. parameters -dir dir1 -file .cpp -dir dir1 -file .hpp
 4361 // will run walkFiles() twice, each time resetting this:
 4362 KeyMap glblCircleMap;
 4363 
 4364 // used with rename: map of simulated output filenames
 4365 KeyMap glblOutFileMap;
 4366 
 4367 #ifndef USE_SFK_BASE
 4368 void resetStats()
 4369 {__
 4370    void resetFileCounter();
 4371    void resetFileSet();
 4372 
 4373    // reset command statistics:
 4374    // copy global settings into local command settings
 4375    memcpy(&cs, &gs, sizeof(cs));
 4376    resetFileCounter();
 4377 
 4378    // reset command settings
 4379    // cs.quiet = 0; // sfk1933 enable global use
 4380    pszGlblSaveTo = 0;
 4381    if (pszGlblSinceDir) { delete [] pszGlblSinceDir; pszGlblSinceDir=0; }
 4382 
 4383    // reset selected dirs and files
 4384    resetFileSet();
 4385 
 4386    // reset all temporary command data
 4387    cleanupTmpCmdData();
 4388 
 4389    // reset circular dependency blocker
 4390    glblCircleMap.reset();
 4391 }
 4392 #endif // USE_SFK_BASE
 4393 
 4394 class FileList;
 4395 class StringPipe;
 4396 class Coi;
 4397 
 4398 int execDetab       (char *pszFile, char *pszOutFile);
 4399 int execEntab       (char *pszFile, char *pszOutFile);
 4400 int execScantab     (char *pszFile);
 4401 int getFileMD5      (char *pszFile, SFKMD5 &md5, bool bSilent=0, bool bInfoCycle=0);
 4402 int getFileMD5      (Coi  *pcoi, SFKMD5 &md5, bool bSilent=0, bool bInfoCycle=0);
 4403 int execFormConv    (char *pszFile, char *pszOutFile);
 4404 int execHexdump     (Coi  *pcoi, uchar *pBuf, uint nBufSize, int iHighOff=-1, int iHighLen=0, FILE *fout=0, num nListOffset=0);
 4405 int walkFiles       (Coi  *pcoi, int lLevel, int &nGlobFiles, FileList &oDirFiles, int &lDirs, num &lBytes, num &ntime1, num &ntime2);
 4406 int execSingleFile  (Coi  *pcoi, int lLevel, int &lGlobFiles, int nDirFileCnt, int &lDirs, num &lBytes, num &ntime1, num &ntime2);
 4407 int execSingleDir   (Coi  *pcoi, int lLevel, int &lGlobFiles, FileList &oDirFiles, int &lDirs, num &lBytes, num &ntime1, num &ntime2);
 4408 int execFileCopy    (Coi  *pcoi);
 4409 int execDirCopy     (char *pszSrc, FileList &oDirFiles);
 4410 int execFileCleanup (char *pszFile);
 4411 int execDirCleanup  (char *pszSrc, FileList &oDirFiles);
 4412 int execFileMove    (Coi  *pcoi);
 4413 int execDirMove     (char *pszSrc, FileList &oDirFiles);
 4414 bool tryGetRelTime  (cchar *psz, num &nRetTime);
 4415 int diffDump        (uchar *p1, uchar *p2, num nlen, num nListOffset, int iHiOff, int iHiLen);
 4416 int execReplaceFix  (Coi *pcoi);
 4417 int execReplaceVar  (Coi *pcoi);
 4418 int execReplaceNew  (Coi *pcoi, char *pszOptOutFile, bool bSameLengthAndFile);
 4419 size_t myfread       (uchar *pBuf, size_t nBytes, FILE *fin , num nMaxInfo=0, num nCur=0, SFKMD5 *pmd5=0);
 4420 size_t myfwrite      (uchar *pBuf, size_t nBytes, FILE *fout, num nMaxInfo=0, num nCur=0, SFKMD5 *pmd5=0);
 4421 char *rootRelativeName(char *pszFileName, char *pszOptRoot);
 4422 char *relativeFilename(char *pszPath);
 4423 void myfgets_init    ( );
 4424 int myfgets         (char *pszOutBuf, int nOutBufLen, FILE *fin, bool *rpBinary=0, char *pAttrBuf=0);
 4425 int execFilter      (Coi *pcoi, FILE *fin = 0, StringPipe *pin = 0, int nMaxLines = -1, char *pszOutFile = 0);
 4426 int execLoad        (Coi *pcoi);
 4427 int execDelFile     (char *pszName);
 4428 int execDelDir      (char *pszName, int lLevel, int &lGlobFiles, FileList &oDirFiles, int &lDirs, num &lBytes, num &nLocalMaxTime, num &ntime2);
 4429 int execVersion     (Coi *pcoi);
 4430 int execMedia       (char *pszSrc, char *pszOutFile);
 4431 int execCsvConv     (bool bToCsv, Coi *pcoi, FILE *fin = 0, StringPipe *pin = 0, int nMaxLines = -1, char *pszOutFile = 0);
 4432 int execRename      (Coi *pcoi);
 4433 int execXRename     (Coi *pcoi);
 4434 int pointedit       (char *pszMaskIn, char *pszSrc, int *pOutMatchLen, char *pszDst, int iMaxDst, bool verb=0);
 4435 int lineedit        (char *pszMaskIn, char *pszSrc, char *pszDst, int iMaxDst, char *pAtt1, char *pAtt2, uint flags, int *poff=0, int *plen=0);
 4436 #ifdef SFKPACK
 4437 int execZipFile     (Coi *pin, int bDir, int iLevel);
 4438 int execUnzip       (char *pszInFile, char *pszSubFile=0, int iOffice=0, char **ppSharedStrings=0);
 4439 int execPackFile    (Coi *pin, Coi *pout, bool bPack);
 4440 #endif // SFKPACK
 4441 #ifdef _WIN32
 4442  #ifdef SFK_W64
 4443  int execFixFile    (ushort *aname, sfkfinddata64_t *pdata);
 4444  #endif
 4445 #endif
 4446 
 4447 // set/provide a minimum info about the current I/O operation.
 4448 // if called multithreaded, this should not crash but in the
 4449 // worst case return only incomplete info strings.
 4450 
 4451 class IOStatus {
 4452 public:
 4453       IOStatus  ( );
 4454 
 4455       void   setInfo (cchar *pinfo);
 4456       char  *getInfo (num &nagemsec, num &nbytes, num &nmaxbytes);
 4457 
 4458       num   countBytes  (num nbytes);
 4459       void  setMaxBytes (num nbytes);
 4460       void  resetBytes  ( );
 4461 
 4462 private:
 4463       char  szClStatus[200+20];
 4464       num   nClSince;
 4465       num   nClBytes;
 4466       num   nClMaxBytes;
 4467 };
 4468 static IOStatus iostat;
 4469 
 4470 IOStatus::IOStatus() {
 4471    memset(this, 0, sizeof(*this));
 4472 }
 4473 
 4474 void IOStatus::setInfo(cchar *pinfo) {
 4475    memset(szClStatus, 0, sizeof(szClStatus));
 4476    if (!pinfo) {
 4477       nClSince = 0;
 4478       return;
 4479    }
 4480    strncpy(szClStatus, pinfo, sizeof(szClStatus)-20);
 4481    nClSince = getCurrentTime();
 4482 }
 4483 
 4484 char *IOStatus::getInfo(num &nagemsec, num &nbytes, num &nmaxbytes) {
 4485    if (!szClStatus[0]) return 0;
 4486    if (nClSince > 0) {
 4487       num nage  = getCurrentTime() - nClSince;
 4488       nagemsec  = nage;
 4489       nbytes    = nClBytes;
 4490       nmaxbytes = nClMaxBytes;
 4491    } else {
 4492       nagemsec  = 0;
 4493       nbytes    = 0;
 4494       nmaxbytes = 0;
 4495    }
 4496    return szClStatus;
 4497 }
 4498 
 4499 num IOStatus::countBytes(num nbytes) {
 4500    nClBytes += nbytes;
 4501    return nClBytes;
 4502 }
 4503 
 4504 void IOStatus::setMaxBytes(num nbytes) {
 4505    nClMaxBytes = nbytes;
 4506 }
 4507 
 4508 void IOStatus::resetBytes() {
 4509    nClBytes = 0;
 4510    nClMaxBytes = 0;
 4511 }
 4512 
 4513 IOStatusPhase::IOStatusPhase(cchar *pinfo) {
 4514    iostat.setInfo(pinfo);
 4515 }
 4516 
 4517 IOStatusPhase::~IOStatusPhase( ) {
 4518    // iostat.setInfo(0);
 4519 }
 4520 
 4521 char *getIOStatus(num &nage, num &nbytes, num &nmaxbytes) {
 4522    return iostat.getInfo(nage, nbytes, nmaxbytes);
 4523 }
 4524 num   countIOBytes(num nbytes)   { return iostat.countBytes(nbytes); }
 4525 void  setIOStatMaxBytes(num n)   { iostat.setMaxBytes(n); }
 4526 void  resetIOBytes( )   { iostat.resetBytes(); }
 4527 void  resetIOStatus( )  { iostat.setInfo(0); iostat.resetBytes(); }
 4528 
 4529 // extended skip functions, ignore line ends,
 4530 // handle quotes and escaped quotes:
 4531 
 4532 void nextToken(char **pp, char &rclast, bool &rbnewline)
 4533 {
 4534    char *p = *pp;
 4535    while (*p && ((*p == ' ') || (*p == '\t') || (*p == '\r') || (*p == '\n') || (*p == '#')) ) {
 4536       // skip # only if it's first char of line
 4537       if (*p == '#' && rclast != '\n')
 4538          break;
 4539       rclast = *p++;
 4540       if (rclast == '\n')
 4541          rbnewline = 1;
 4542    }
 4543    *pp = p;
 4544 }
 4545 
 4546 void skipPastToken(char **pp, char &rclast)
 4547 {
 4548    char *p = *pp;
 4549    bool besc = 0;
 4550    char cold = 0;
 4551    while (*p)
 4552    {
 4553       if (*p == '\"' && cold != '\\')
 4554          besc ^= 0x1;
 4555       if (!besc && ((*p == ' ') || (*p == '\t') || (*p == '\r') || (*p == '\n')) )
 4556          break;
 4557       rclast = cold = *p++;
 4558    }
 4559    *pp = p;
 4560 }
 4561 
 4562 // .
 4563 // strip quotes, transform inner quotes \" to normal quotes "
 4564 // uses: szLineBuf, szLineBuf2
 4565 // sfk181: no longer returns 1 on empty token
 4566 int postProcessToken(char **pptok, StringTable &oDynaStrings, int argc, char *argv[], int iParmOffs, int nParm)
 4567 {
 4568    bool bquoted = 0;
 4569 
 4570    char *ptok = *pptok;
 4571 
 4572    // strip outer quotes, if any
 4573    if (*ptok == '\"') {
 4574       // strip quotes
 4575       int nlen = strlen(ptok);
 4576       if (ptok[nlen-1] != '\"')
 4577          return 9; // missing end quote
 4578       bquoted = 1;
 4579       memmove(ptok, ptok+1, nlen-1);
 4580       nlen -= 2;         // count both quotes
 4581       ptok[nlen] = '\0'; // readjust terminator
 4582       if (cs.trimscript) {
 4583          char *pscur=ptok;
 4584          char *pdcur=ptok;
 4585          // drop initial blank line, if any
 4586          if (!strncmp(pscur,"\r\n",2)) pscur+=2;
 4587          else if (*pscur=='\n') pscur++;
 4588          while (*pscur) {
 4589             char *peol=pscur;
 4590             while (*peol && !iseol(*peol)) peol++;
 4591             char *psnex=peol;
 4592             // reduce left
 4593             while (*pscur && isws(*pscur)) pscur++;
 4594             // do not reduce rite
 4595             // while (peol>pscur && isws(peol[-1])) peol--;
 4596             // copy remainder
 4597             while (pscur<peol)
 4598                *pdcur++=*pscur++;
 4599             // skip eol, now in psnex
 4600             while (iseol(*psnex))
 4601                psnex++;
 4602             // continue on next line
 4603             pscur=psnex;
 4604          }
 4605          // drop trailing blank line, if any
 4606          // \r\n\r\n or \n\n
 4607          if (pdcur>=ptok+4 && !strncmp(pdcur-4,"\r\n\r\n",4)) pdcur-=2;
 4608          else
 4609          if (pdcur>=ptok+2 && !strncmp(pdcur-2,"\n\n",2)) pdcur-=1;
 4610          *pdcur='\0';
 4611       }
 4612    }
 4613 
 4614    // transform inner quotes, if any
 4615    char *psz = ptok;
 4616    while (*psz) {
 4617       if (!strncmp(psz, "\\\"", 2)) {
 4618          int nlen = strlen(psz);
 4619          memmove(psz, psz+1, nlen-1);   // cut backslash
 4620          psz[nlen-1] = '\0'; // new terminator
 4621       }
 4622       if (*psz) psz++;
 4623    }
 4624 
 4625    // substitute parameters. accept both $1,$2,$3 and %1,%2,%3.
 4626    strcopy(szLineBuf, ptok);
 4627    char szName1[50],szName2[50];
 4628    mclear(szName1);
 4629    mclear(szName2);
 4630    bool bDoneAny = 0;
 4631 
 4632    // accept "$" and "%" parameters, or user defined?
 4633    bool buserpre = cs.paramprefix[0] ? 1 : 0;
 4634 
 4635    for (int iparm=0; iparm<9; iparm++)
 4636    {
 4637       char *pcur = szLineBuf;
 4638       while (1)
 4639       {
 4640          int nlen1=0,nlen2=0;
 4641          if (buserpre) {
 4642             sprintf(szName1, "%.20s%d" , cs.paramprefix, (int)(iparm+1));
 4643             nlen1 = strlen(szName1);
 4644          } else {
 4645             sprintf(szName1, "$%d" , (int)(iparm+1));
 4646             sprintf(szName2, "%%%d", (int)(iparm+1));
 4647             nlen1 = strlen(szName1);
 4648             nlen2 = strlen(szName2);
 4649          }
 4650          char *psz  = pcur;
 4651          char clast = (char)-1, cesc = 0;
 4652          int nlenx = 0;
 4653          while (*psz) {
 4654             if (!strncmp(psz, szName1, nlen1)) {
 4655                nlenx = nlen1;
 4656                // allow escapes only if not user defined
 4657                if (!buserpre) cesc = '$';
 4658                break;
 4659             }
 4660             if (!buserpre && !strncmp(psz, szName2, nlen2))
 4661                { nlenx = nlen2; cesc = '%'; break; }
 4662             clast = *psz++;
 4663          }
 4664          if (!*psz) break;
 4665          if (clast == cesc) {
 4666             /*
 4667                // do NOT replace parm, just unescape it:
 4668                psz--;
 4669                memmove(psz, psz+1, strlen(psz+1)+1); // WITH terminator
 4670                pcur = psz+1;
 4671                // remember we have to dup szLineBuf
 4672                bDoneAny = 1;
 4673                continue;
 4674             */
 4675          }
 4676          // replace $param... by actual value
 4677          cchar *pval = "";
 4678          // fix sfk193: iparm >= 0 check
 4679          if (iparm >= 0 && iparm < nParm && iParmOffs+iparm < argc)
 4680             pval = argv[iParmOffs+iparm];
 4681          int nleft = psz-pcur;
 4682          memcpy(szLineBuf2, pcur, nleft);
 4683          int nmid  = strlen(pval);
 4684          if (nmid > 0)
 4685             memcpy(szLineBuf2+nleft, pval, nmid);
 4686          char *prite= psz+nlenx;
 4687          strcpy(szLineBuf2+nleft+nmid, prite);
 4688          strcpy(pcur, szLineBuf2);
 4689          // step pcur, may have to replace $1 many times
 4690          pcur += nleft+nmid;
 4691          // remember we have to dup szLineBuf
 4692          bDoneAny = 1;
 4693       }
 4694    }
 4695 
 4696    // do we have to manage a copy of the token?
 4697    if (bDoneAny) 
 4698    {
 4699       // sfk1812: remove empty tokens
 4700       if (!bquoted && !szLineBuf[0])
 4701          return 1;
 4702 
 4703       // sfk181: no special handling of empty tokens.
 4704       if (oDynaStrings.addEntry(szLineBuf, -1, &ptok)) // is copied
 4705          return 9+perr("outofmem");
 4706       // ptok should now contain the copy
 4707       if (!ptok) return 9+perr("internal #2310271958");
 4708       // write back ptok, in case it was changed:
 4709       *pptok = ptok;
 4710    }
 4711 
 4712    return 0;
 4713 }
 4714 
 4715 int fixMultiLineParm(char *ptok, char cmode, int iind=0)
 4716 {
 4717    if (!strchr(ptok, '\n'))
 4718       return 1; // nothing to do
 4719 
 4720    if (cs.mlquotes == 'r')
 4721       return 1;
 4722 
 4723    if (cs.mlquotes != 'f') {
 4724       static bool btold=0;
 4725       if (!btold) {
 4726          btold=1;
 4727          // pwarn("missing -qraw or -qtrim with multi line quotes. type \"sfk label\" for help.\n");
 4728       }
 4729       return 1;
 4730    }
 4731 
 4732    // printf("---%s\n---\n",dataAsTrace(ptok,strlen(ptok)));
 4733 
 4734    bool bTrim   = (cmode=='t') ? 1 : 0;  // without extra blank
 4735    bool bParms  = (cmode=='p') ? 1 : 0;  // with extra blank
 4736    bool bIndent = (cmode=='i') ? 1 : 0;  // auto indent keeping lf
 4737 
 4738    char *pscur=ptok;
 4739    char *pdcur=ptok;
 4740    char *pdmax=ptok+strlen(ptok);
 4741    int   iparm=0;
 4742    int   ibase=0; // base indent
 4743 
 4744    // drop initial blank line, if any
 4745    if (!strncmp(pscur,"\r\n",2)) pscur+=2;
 4746    else if (*pscur=='\n') pscur++;
 4747 
 4748    while (*pscur!=0 && pdcur<pdmax)
 4749    {
 4750       char *peol=pscur;
 4751       bool  ballws=1;
 4752       while (*peol && !iseol(*peol)) {
 4753          if (!isws(*peol))
 4754             ballws=0;
 4755          peol++;
 4756       }
 4757       char *psnex=peol;
 4758 
 4759       // drop trailing blank line, if any
 4760       if (bIndent && !*peol && ballws)
 4761          break;
 4762 
 4763       // reduce left
 4764       if (bIndent) {
 4765          if (iparm == 0) {
 4766             // first line: find base index
 4767             int i=0;
 4768             while (pscur[i] && isws(pscur[i]))
 4769                i++;
 4770             if (iind>0) {
 4771                if (iind<i)
 4772                   i -= iind;
 4773                else
 4774                   i = 0;
 4775             }
 4776             pscur += i;
 4777             ibase += i;
 4778          } else {
 4779             // further lines: reduce only base index
 4780             int irange=ibase;
 4781             while (*pscur && isws(*pscur) && irange)
 4782                { pscur++; irange--; }
 4783          }
 4784       } else {
 4785          while (*pscur && isws(*pscur)) pscur++;
 4786       }
 4787 
 4788       // do not reduce rite
 4789       // while (peol>pscur && isws(peol[-1])) peol--;
 4790 
 4791       // insert parm separator blank?
 4792       if (bParms && iparm && (pdcur<pdmax))
 4793          *pdcur++=' ';
 4794 
 4795       // copy remainder
 4796       while (pscur<peol && pdcur<pdmax)
 4797          *pdcur++ = *pscur++;
 4798 
 4799       iparm++;
 4800 
 4801       if (bIndent) {
 4802          // copy eol, now in psnex
 4803          while (iseol(*psnex)!=0 && pdcur<pdmax) {
 4804             if (*psnex == '\r')
 4805                psnex++;
 4806             else
 4807                *pdcur++ = *psnex++;
 4808          }
 4809       } else {
 4810          // skip eol, now in psnex
 4811          while (iseol(*psnex))
 4812             psnex++;
 4813       }
 4814 
 4815       // continue on next line
 4816       pscur=psnex;
 4817    }
 4818 
 4819    // last line must not end with LF, is added on output
 4820    if (bIndent!=0 && pdcur>ptok && pdcur[-1]=='\n')
 4821       pdcur--;
 4822 
 4823    *pdcur='\0';
 4824 
 4825    // printf("---%s\n---\n",dataAsTrace(ptok,strlen(ptok)));
 4826 
 4827    if (cs.debug) {
 4828       printf("--- multi line parm changed using mode %c: ---\n",cmode);
 4829       printf("%s\n---\n",ptok);
 4830    }
 4831 
 4832    return 0;
 4833 }
 4834 
 4835 char *mystrrstr(char *psrc, cchar *ppat)
 4836 {
 4837    if (!psrc || !ppat) return 0;
 4838 
 4839    char c = *ppat;
 4840    int nsrclen = strlen(psrc);
 4841    int npatlen = strlen(ppat);
 4842    if (npatlen > nsrclen) return 0;
 4843 
 4844    char *pcur = psrc + nsrclen - npatlen;
 4845    while (pcur >= psrc) {
 4846       if (*pcur == c && !strncmp(pcur, ppat, npatlen))
 4847          return pcur;
 4848       pcur--;
 4849    }
 4850    return 0;
 4851 }
 4852 
 4853 char *mystrristr(char *psrc, cchar *ppat)
 4854 {
 4855    if (!psrc || !ppat) return 0;
 4856 
 4857    int nsrclen = strlen(psrc);
 4858    int npatlen = strlen(ppat);
 4859    if (npatlen > nsrclen) return 0;
 4860 
 4861    char *pcur = psrc + nsrclen - npatlen;
 4862    while (pcur >= psrc) {
 4863       if (!mystrnicmp(pcur, ppat, npatlen))
 4864          return pcur;
 4865       pcur--;
 4866    }
 4867    return 0;
 4868 }
 4869 
 4870 int   nGlblConsColumns    = 80;
 4871 bool  bGlblConsColumnsSet =  0;
 4872 int   nGlblConsRows       = 30;
 4873 bool  bGlblConsRowsSet    =  0;
 4874 
 4875 int setWriteEnabled(char *pszFile)
 4876 {
 4877    #ifdef _WIN32
 4878 
 4879    BOOL bok = 0;
 4880 
 4881    #ifdef WINFULL
 4882    WIN32_FILE_ATTRIBUTE_DATA oinf;
 4883    bok = GetFileAttributesEx(pszFile, GetFileExInfoStandard, &oinf);
 4884    if (!bok) return 9;
 4885 
 4886    oinf.dwFileAttributes &= (0xFFFFFFFFUL ^ FILE_ATTRIBUTE_READONLY);
 4887    bok = SetFileAttributes(pszFile, oinf.dwFileAttributes);
 4888    #else
 4889    uint nattrib = GetFileAttributesA(pszFile);
 4890    nattrib &= (0xFFFFFFFFUL ^ FILE_ATTRIBUTE_READONLY);
 4891    SetFileAttributes(pszFile, nattrib);
 4892    #endif
 4893 
 4894    if (!bok) return 9;
 4895 
 4896    #else
 4897 
 4898    struct stat64 buf;
 4899    if (stat64(pszFile, &buf)) return 9;
 4900 
 4901    mode_t nmode = buf.st_mode | _S_IWRITE;
 4902    if (chmod(pszFile, nmode)) return 9;
 4903  
 4904    #endif
 4905 
 4906    return 0;
 4907 }
 4908 
 4909 FILE *pGlblOpenWriteFile   = 0;
 4910 char  szGlblOpenWriteName[MAX_LINE_LEN+10];
 4911 
 4912 void beginFileWrite(char *pszName, FILE *p) {
 4913    pGlblOpenWriteFile = p;
 4914    strcopy(szGlblOpenWriteName, pszName);
 4915 }
 4916 void endFileWrite() {
 4917    pGlblOpenWriteFile = 0;
 4918    szGlblOpenWriteName[0] = 0;
 4919 }
 4920 void checkFileWrite() {
 4921    if (pGlblOpenWriteFile)
 4922       perr("unexpected: file left open for write: %s\n", szGlblOpenWriteName);
 4923 }
 4924 void cleanupFileWrite() {
 4925    if (pGlblOpenWriteFile) {
 4926       // close and remove incomplete (trash) file.
 4927       // may not work here as this is called during interrupt.
 4928       fclose(pGlblOpenWriteFile);
 4929       remove(szGlblOpenWriteName);
 4930    }
 4931 }
 4932 FILE *myfopen(char *pszName, cchar *pszMode) {
 4933    FILE *f = fopen(pszName, pszMode);
 4934    if (!f && !strcmp(pszMode, "wb") && fileExists(pszName)) {
 4935       // file is probably write protected
 4936       if (setWriteEnabled(pszName))
 4937          return 0;
 4938       // retry on write-enabled file
 4939       f = fopen(pszName, pszMode);
 4940    }
 4941    if (f) {
 4942       if (pGlblOpenWriteFile)
 4943          pwarn("open for write on %s not registered\n", pszName);
 4944       else
 4945          beginFileWrite(pszName, f);
 4946    }
 4947    return f;
 4948 }
 4949 void myfclose(FILE *f) {
 4950    if (f == pGlblOpenWriteFile)
 4951       endFileWrite();
 4952    fclose(f);
 4953 }
 4954 
 4955 bool bGlblAllowCtrlCExit = 1;
 4956 
 4957 class CtrlCCover {
 4958 public:
 4959       CtrlCCover  (bool bAllowExit);
 4960      ~CtrlCCover  ( );
 4961 };
 4962 
 4963 CtrlCCover::CtrlCCover(bool b)
 4964    { bGlblAllowCtrlCExit = b; }
 4965  
 4966 CtrlCCover::~CtrlCCover( )
 4967    { bGlblAllowCtrlCExit = 1; }
 4968 
 4969 #define DisableCtrlCProcessExit() CtrlCCover oCtrlCCover(0)
 4970 
 4971 #ifdef _WIN32
 4972 HANDLE hGlblConsole     =  0;
 4973 WORD   nGlblConsAttrib  =  0;
 4974 
 4975 #define CCMASK_FOREGROUND (FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY)
 4976 #define CCMASK_BACKGROUND (BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE|BACKGROUND_INTENSITY)
 4977 #define CCMASK_FORE_BACK  (CCMASK_FOREGROUND|CCMASK_BACKGROUND)
 4978 #define CCMASK_NOT_FGCOL  (0xFFFF ^ CCMASK_FOREGROUND)
 4979 
 4980 // need this to ensure that commands dumping colored output
 4981 // do never leave the shell in a non-std color.
 4982 BOOL WINAPI ctrlcHandler(DWORD type)
 4983 {
 4984    if (type != CTRL_C_EVENT && type != CTRL_BREAK_EVENT)
 4985       return 0;
 4986 
 4987    bGlblEscape = 1;
 4988  
 4989    setTextColor(-1);
 4990 
 4991    if (bGlblAllowCtrlCExit)
 4992    {
 4993       // do this only with exit allowed,
 4994       // otherwise main program may crash
 4995       // as it continues on the file handle.
 4996       cleanupFileWrite();
 4997       ExitProcess(8);
 4998    }
 4999 
 5000    return 1;
 5001 }
 5002 #else
 5003 // unix todo: ctrl+c handler to reset color
 5004 void ctrlcHandler(int sig_number)
 5005 {
 5006    bGlblEscape = 1;
 5007  
 5008    setTextColor(-1, 0); // stdout
 5009    setTextColor(-1, 1); // stderr
 5010 
 5011    if (bGlblAllowCtrlCExit)
 5012    {
 5013       // do this only with exit allowed,
 5014       // otherwise main program may crash
 5015       // as it continues on the file handle.
 5016       cleanupFileWrite();
 5017       exit(8);
 5018    }
 5019 }
 5020 #endif
 5021 
 5022 void setColorScheme(cchar *psz1)
 5023 {
 5024    char *psz2 = 0;
 5025    bool bany = 0;
 5026 
 5027    if (!strncmp(psz1, "off", 3)) {
 5028       gs.usecolor = gs.usehelpcolor = 0;
 5029       cs.usecolor = cs.usehelpcolor = 0; 
 5030    }
 5031    else
 5032    if (!strncmp(psz1, "on", 2)) {
 5033       gs.usecolor = 1;
 5034       cs.usecolor = 1;
 5035    }
 5036 
 5037    if (strstr(psz1, "bright"))
 5038       { nGlblDarkColBase = 1; bany=1; }
 5039 
 5040    if (strstr(psz1, "dark"))
 5041       { nGlblBrightColBase = 0; bany=1; }
 5042 
 5043    #ifdef MAC_OS_X // 1694
 5044    if (strstr(psz1, "theme:black")) {
 5045       nGlblDarkColBase    =  0;
 5046       nGlblBrightColBase  =  0;
 5047       nGlblHeadColor      =  4;
 5048       nGlblExampColor     =  6;
 5049       nGlblFileColor      = 10;
 5050       nGlblHitColor       =  4;
 5051       nGlblRepColor       =  6;
 5052       nGlblLinkColor      = 12;
 5053       nGlblWarnColor      =  6;
 5054       nGlblDefColor       = 14;
 5055       bany=1;
 5056    }
 5057    if (strstr(psz1, "theme:white")) {
 5058       nGlblDarkColBase    =  0;
 5059       nGlblBrightColBase  =  0;
 5060       nGlblHeadColor      =  4;
 5061       nGlblExampColor     = 10;
 5062       nGlblFileColor      = 10;
 5063       nGlblLinkColor      = 12;
 5064       nGlblHitColor       =  4;
 5065       nGlblRepColor       =  6;
 5066       nGlblErrColor       =  2;
 5067       nGlblWarnColor      = 10;
 5068       nGlblPreColor       =  8;
 5069       nGlblTimeColor      =  8;
 5070       nGlblTraceIncColor  = 12;
 5071       nGlblTraceExcColor  = 10;
 5072       nGlblDefColor       =  0;
 5073       bany=1;
 5074    }
 5075    #else
 5076    if (strstr(psz1, "theme:black")) {
 5077       // bright colors with black background
 5078       nGlblDarkColBase =  1;
 5079       nGlblExampColor  =  7; // bright yellow
 5080       nGlblHeadColor   =  5; // green
 5081       nGlblFileColor   =  5; // green
 5082       nGlblHitColor    = 11; // purple
 5083       nGlblRepColor    =  7; // bright yellow
 5084       nGlblLinkColor   = 13; // bright cyan
 5085       nGlblWarnColor   =  7; // bright yellow
 5086       #ifndef _WIN32
 5087       // nGlblDefColor = 14; // fix: 1732: don't use gray but terminal default
 5088       #endif
 5089       bany=1;
 5090    }
 5091    if (strstr(psz1, "theme:white")) {
 5092       // with white background, user may want to select
 5093       // bright or dark colors case by case.
 5094       nGlblHeadColor      =  4; // green
 5095       nGlblExampColor     = 11; // purple
 5096       nGlblFileColor      =  4; // green
 5097       nGlblHitColor       = 11; // purple
 5098       nGlblRepColor       =  3; // red
 5099       nGlblLinkColor      = 12; // cyan
 5100       nGlblErrColor       =  3; // red
 5101       nGlblWarnColor      = 11; // purple
 5102       nGlblPreColor       =  8; // blue
 5103       nGlblTimeColor      =  8; // blue
 5104       nGlblTraceIncColor  = 12; // dark magenta
 5105       nGlblTraceExcColor  = 11; // purple
 5106       bany=1;
 5107    }
 5108    #endif
 5109 
 5110    if (strstr(psz1, ",nobold")) // 1694
 5111       { nGlblDarkColBase = 0; nGlblBrightColBase = 0; bany=1; }
 5112    else
 5113    if (strstr(psz1, ",bold"))   // 1694
 5114       { nGlblDarkColBase = 1; nGlblBrightColBase = 1; bany=1; }
 5115 
 5116    psz2 = (char*)strstr(psz1, "head:");  if (psz2) { nGlblHeadColor  = atol(psz2+5); bany=1; }
 5117    psz2 = (char*)strstr(psz1, "examp:"); if (psz2) { nGlblExampColor = atol(psz2+6); bany=1; }
 5118    psz2 = (char*)strstr(psz1, "file:");  if (psz2) { nGlblFileColor  = atol(psz2+5); bany=1; }
 5119    psz2 = (char*)strstr(psz1, "link:");  if (psz2) { nGlblLinkColor  = atol(psz2+5); bany=1; }
 5120    psz2 = (char*)strstr(psz1, "hit:");   if (psz2) { nGlblHitColor   = atol(psz2+4); bany=1; }
 5121    psz2 = (char*)strstr(psz1, "rep:");   if (psz2) { nGlblRepColor   = atol(psz2+4); bany=1; }
 5122    psz2 = (char*)strstr(psz1, "err:");   if (psz2) { nGlblErrColor   = atol(psz2+4); bany=1; }
 5123    psz2 = (char*)strstr(psz1, "warn:");  if (psz2) { nGlblWarnColor  = atol(psz2+5); bany=1; }
 5124    psz2 = (char*)strstr(psz1, "pre:");   if (psz2) { nGlblPreColor   = atol(psz2+4); bany=1; }
 5125    #ifndef _WIN32
 5126    psz2 = (char*)strstr(psz1, "def:");   if (psz2) { nGlblDefColor   = atol(psz2+4); bany=1; }
 5127    #endif
 5128    //                   0123456789
 5129    psz2 = (char*)strstr(psz1, "time:");     if (psz2) { nGlblTimeColor     = atol(psz2+5); bany=1; }
 5130    psz2 = (char*)strstr(psz1, "traceinc:"); if (psz2) { nGlblTraceIncColor = atol(psz2+9); bany=1; }
 5131    psz2 = (char*)strstr(psz1, "traceexc:"); if (psz2) { nGlblTraceExcColor = atol(psz2+9); bany=1; }
 5132  
 5133    if (bany) {
 5134       gs.usecolor = 1;
 5135       cs.usecolor = 1;
 5136    }
 5137 }
 5138 
 5139 int nGlblCurColor = -1; // currently active color
 5140 
 5141 void setTextColor(int nIn, bool bStdErr, bool bVerbose)
 5142 {
 5143    int n = nIn;
 5144 
 5145    if (n == nGlblCurColor) {
 5146       mtklog(("color: ignore %d, is set already", n));
 5147       return;
 5148    }
 5149    nGlblCurColor = n;
 5150 
 5151    if (bGlblHtml)
 5152    {
 5153       static bool bAnySet = 0;
 5154       static bool bIsBold = 0;
 5155       uint ncol = 0;
 5156       if (bAnySet) { printf("</font>"); bAnySet=0; }
 5157       if (bIsBold) { printf("</b>"); bIsBold=0; }
 5158       if (n == -1)
 5159          return;
 5160       bAnySet=1;
 5161       if (n == 1) { bIsBold=1; printf("<b>"); }
 5162       if (n &  2) ncol |= (n&1) ? 0xFF0000 : 0x990000;
 5163       if (n &  4) ncol |= (n&1) ? 0x00FF00 : 0x009900;
 5164       if (n &  8) ncol |= (n&1) ? 0x0000FF : 0x000099;
 5165       if (n & 16) { bIsBold=1; printf("<b>"); }
 5166       printf("<font color=\"%06X\">", ncol);
 5167       return;
 5168    }
 5169 
 5170    if (!cs.usecolor) {
 5171       mtklog(("color: ignore %d, no colors used", n));
 5172       return;
 5173    }
 5174 
 5175    #ifdef _WIN32
 5176 
 5177    #ifdef WINFULL
 5178    if (n == 0) {
 5179       // FIX: n==0 produces invisible text both with a white
 5180       //      or black background shell. a visible compromise:
 5181       n = 14;
 5182    }
 5183 
 5184    WORD nAttrib = 0;
 5185    if (n & 1) nAttrib |= FOREGROUND_INTENSITY;
 5186    if (n & 2) nAttrib |= FOREGROUND_RED;
 5187    if (n & 4) nAttrib |= FOREGROUND_GREEN;
 5188    if (n & 8) nAttrib |= FOREGROUND_BLUE;
 5189 
 5190    if (n == -1) {
 5191       // default color: set all attribs as they were.
 5192       SetConsoleTextAttribute(hGlblConsole, nGlblConsAttrib);
 5193       mtklog(("color: scta %lxh (is default)", nGlblConsAttrib));
 5194    } else {
 5195       // set new FOREGROUND text color, but make sure that
 5196       // background color and anything else stays unchanged.
 5197       uint nval = (nGlblConsAttrib & CCMASK_NOT_FGCOL) | nAttrib;
 5198       SetConsoleTextAttribute(hGlblConsole, nval);
 5199       mtklog(("color: scta %lxh (default=%lxh)", nval, nGlblConsAttrib));
 5200    }
 5201    #endif
 5202 
 5203    #else
 5204 
 5205    #define UXATTR_RESET     0
 5206    #define UXATTR_BRIGHT    1
 5207    #define UXATTR_DIM       2
 5208    #define UXATTR_UNDERLINE 3
 5209    #define UXATTR_BLINK     4
 5210    #define UXATTR_REVERSE   7
 5211    #define UXATTR_HIDDEN    8
 5212 
 5213    #define UXCOL_BLACK      0
 5214    #define UXCOL_RED        1
 5215    #define UXCOL_GREEN      2
 5216    #define UXCOL_YELLOW     3
 5217    #define UXCOL_BLUE       4
 5218    #define UXCOL_MAGENTA    5
 5219    #define UXCOL_CYAN       6
 5220    #define UXCOL_WHITE      7
 5221 
 5222    FILE *fout = stdout;
 5223    if (bStdErr)
 5224          fout = stderr;
 5225 
 5226    if (n==-1) {
 5227       // unix: have to use a user-defined default color
 5228       n = nGlblDefColor;
 5229    }
 5230 
 5231    int nAttr = (n & 1) ? UXATTR_BRIGHT : UXATTR_RESET;
 5232 
 5233    switch (n & 14) {
 5234       case  2: n = UXCOL_RED;     break;
 5235       case  4: n = UXCOL_GREEN;   break;
 5236       case  6: n = UXCOL_YELLOW;  break;
 5237       case  8: n = UXCOL_BLUE;    break;
 5238       case 10: n = UXCOL_MAGENTA; break;
 5239       case 12: n = UXCOL_CYAN;    break;
 5240       case 14: n = UXCOL_WHITE;   break;
 5241       default: n = UXCOL_BLACK;   break;
 5242    }
 5243 
 5244    if (nIn == -1 && nGlblDefColor == 0) {
 5245       // fix: 1732: linux default color if def:0
 5246       if (bVerbose)
 5247          fprintf(fout, "using color sequence: \\x1b[0;0m\n");
 5248       fprintf(fout, "%c[0;0m", 0x1B);
 5249    } else {
 5250       if (bVerbose)
 5251          fprintf(fout, "using color sequence: \\x1b[%d;%dm\n", nAttr, n+30);
 5252       fprintf(fout, "%c[%d;%dm", 0x1B, nAttr, n+30);
 5253    }
 5254 
 5255    #endif
 5256 }
 5257 
 5258 void oprintf(cchar *pszFormat, ...);
 5259 void oprintf(StringPipe *pOutData, cchar *pszFormat, ...);
 5260 
 5261 uchar unicodeToIso(uint ucode)
 5262 {
 5263    switch (ucode)
 5264    {
 5265       // normalize stupid apostrophes
 5266       case 0x2018: ucode = 0x27; break;
 5267       case 0x2019: ucode = 0x27; break;
 5268       case 0x0000: ucode = cs.toisodef; break;
 5269       default:
 5270          // replace all non-8bit by default
 5271          if (ucode >= 0x0100)
 5272             ucode = cs.toisodef;
 5273             break;
 5274    }
 5275    return (uchar)ucode;
 5276 }
 5277 
 5278 void utf8ToIso(char *psz, int *pChg=0)
 5279 {
 5280    UTF8Codec utf(psz);
 5281    char *pDstCur = psz;
 5282    char *pDstMax = psz+strlen(psz);
 5283    uint ucode = 0;
 5284    while (pDstCur<pDstMax && utf.hasChar()!=0)
 5285    {
 5286       *pDstCur++ = (char)unicodeToIso(utf.nextChar());
 5287    }
 5288    if (pDstCur<pDstMax) {
 5289       *pDstCur = '\0';
 5290       if (pChg) (*pChg)++;
 5291    }
 5292 }
 5293 
 5294 void changeLineCase(char *psz, int iMode, int *pChg=0)
 5295 {
 5296    char u;
 5297    switch (iMode) {
 5298       case 1:
 5299          for (;*psz;psz++) {
 5300             u = sfktoupper(*psz);
 5301             if (*psz != u) {
 5302                *psz = u;
 5303                if (*pChg) (*pChg)++;
 5304             }
 5305          }
 5306          break;
 5307       case 2:
 5308          for (;*psz;psz++) {
 5309             u = sfktolower(*psz);
 5310             if (*psz != u) {
 5311                *psz = u;
 5312                if (*pChg) (*pChg)++;
 5313             }
 5314          }
 5315          break;
 5316    }
 5317 }
 5318 
 5319 void trimLine(char *psz, int iMode, int *pChg=0)
 5320 {
 5321    char *pleft  = psz;
 5322    char *prite  = psz + strlen(psz);
 5323    char *prite2 = prite;
 5324    int   iChg   = 0;
 5325 
 5326    if (iMode & 1)
 5327    {
 5328       while (*pleft == ' ' || *pleft == '\t') {
 5329          pleft++;
 5330          iChg++;
 5331       }
 5332    }
 5333 
 5334    if (iMode & 2)
 5335    {
 5336       while (prite > pleft && (prite[-1] == ' ' || prite[-1] == '\t')) {
 5337          prite--;
 5338          iChg++;
 5339       }
 5340    }
 5341 
 5342    int ilen = prite - pleft;
 5343 
 5344    if (pleft != psz)
 5345    {
 5346       // move and terminate
 5347       for (int i=0; i<ilen; i++)
 5348          psz[i] = pleft[i];
 5349       psz[ilen] = '\0';
 5350       if (pChg) *pChg = iChg;
 5351    }
 5352    else if (prite != prite2)
 5353    {
 5354       // just terminate
 5355       *prite = '\0';
 5356       if (pChg) *pChg = iChg;
 5357    }
 5358 }
 5359 
 5360 char szPrintBufMap[MAX_LINE_LEN+10];
 5361 
 5362 void mystrcatf(char *pOut, int nOutMax, cchar *pszFormat, ...)
 5363 {
 5364    va_list argList;
 5365    va_start(argList, pszFormat);
 5366    ::vsnprintf(szPrintBufMap, sizeof(szPrintBufMap)-10, pszFormat, argList);
 5367    szPrintBufMap[sizeof(szPrintBufMap)-10] = '\0';
 5368    char *psz = szPrintBufMap;
 5369 
 5370    if (nOutMax == 0) nOutMax = MAX_LINE_LEN;
 5371 
 5372    int nlen1 = strlen(pOut);
 5373    int nrem1 = (nOutMax - nlen1) - 1; // including term.
 5374    int nlen2 = strlen(psz);
 5375    if (nlen2 > nrem1) nlen2 = nrem1;
 5376    if (nlen2 > 0) {
 5377       memcpy(pOut+nlen1, psz, nlen2);
 5378       *(pOut+nlen1+nlen2) = '\0';
 5379    }
 5380 }
 5381 
 5382 // without zero termination, only for short fixed-size strings
 5383 void mystrplot(char *pOut, int iMaxOut, cchar *pszFormat, ...)
 5384 {
 5385    va_list argList;
 5386    va_start(argList, pszFormat);
 5387    ::vsnprintf(szPrintBufMap, sizeof(szPrintBufMap)-10, pszFormat, argList);
 5388    szPrintBufMap[sizeof(szPrintBufMap)-10] = '\0';
 5389    char *psz = szPrintBufMap;
 5390 
 5391    int iCopy = strlen(psz);
 5392    if (iCopy > iMaxOut)
 5393       iCopy = iMaxOut;
 5394  
 5395    memcpy(pOut, psz, iCopy);
 5396 }
 5397 
 5398 char myrchar(char *psz)
 5399 {
 5400    int ilen = strlen(psz);
 5401    if (ilen > 0)
 5402       return psz[ilen-1];
 5403    return 0;
 5404 }
 5405 
 5406 void printHtml(char *pszText, int iTextLen)
 5407 {
 5408    for (int i=0; i<iTextLen; i++) {
 5409       char c = pszText[i];
 5410       switch (c) {
 5411          case '>': printf("&gt;"); break;
 5412          case '<': printf("&lt;"); break;
 5413          case '&': printf("&amp;"); break;
 5414          default : putchar(c); break;
 5415       }
 5416    }
 5417 }
 5418 
 5419 int sfkMapAttrToColor(char cAttr)
 5420 {
 5421    int d = nGlblDarkColBase;
 5422    int b = nGlblBrightColBase;
 5423 
 5424    switch (cAttr)
 5425    {
 5426       // sfk internal color codes. when extending this,
 5427       // also adapt color mapping in the "view" command.
 5428 
 5429       case 'f': return nGlblFileColor ; break;
 5430       case 'l': return nGlblLinkColor ; break;
 5431       case 'h': return nGlblHeadColor ; break;
 5432       case 'i': return nGlblHitColor  ; break;
 5433       case 'a': return nGlblRepColor  ; break;
 5434       case 'x': return nGlblExampColor; break;
 5435       case 'e': return nGlblErrColor  ; break;
 5436       case 'w': return nGlblWarnColor ; break;
 5437       case 't': return nGlblTimeColor ; break;
 5438       case 'p': return nGlblPreColor  ; break;
 5439 
 5440       // bright base colors
 5441       case 'R': return b+2 ; break;
 5442       case 'G': return b+4 ; break;
 5443       case 'B': return b+8 ; break;
 5444       case 'Y': return b+6 ; break;
 5445       case 'C': return b+12; break;
 5446       case 'M': return b+10; break;
 5447       case 'V': return b+14; break;
 5448 
 5449       // dark base colors
 5450       case 'r': return d+2 ; break;
 5451       case 'g': return d+4 ; break;
 5452       case 'b': return d+8 ; break;
 5453       case 'y': return d+6 ; break;
 5454       case 'c': return d+12; break;
 5455       case 'm': return d+10; break;
 5456       case 'v': return d+14; break;
 5457    }
 5458 
 5459    // default:
 5460    return -1;
 5461 }
 5462 
 5463 void printColorText(char *pszText, char *pszAttrib, bool bWithLF)
 5464 {
 5465    // printf("\n\n\n--- pct.text/attr: ---\n%s\n", pszText);
 5466    // printf("%s\n--- output: ---\n", pszAttrib);
 5467 
 5468    int nTextLen = strlen(pszText);
 5469    int nAttrLen = strlen(pszAttrib);
 5470 
 5471    // if insufficient attribs provided, use no color
 5472    if (nAttrLen < nTextLen) {
 5473       oprintf("%s%s", pszText, bWithLF ? "\n":"");
 5474       return;
 5475    }
 5476 
 5477    int i1=0,i2=0;
 5478    while (i1 < nTextLen)
 5479    {
 5480       // identify next phrase of same color
 5481       uchar a1 = pszAttrib[i1];
 5482       for (i2=1; i1+i2<nTextLen; i2++)
 5483          if (pszAttrib[i1+i2] != a1)
 5484             break;
 5485 
 5486       // dump next phrase with len i2.
 5487       int ncolor = sfkMapAttrToColor(a1);
 5488       setTextColor(ncolor);
 5489 
 5490       if (bGlblHtml)
 5491          printHtml(pszText+i1, (int)i2);
 5492       else
 5493          oprintf("%.*s", (int)i2, pszText+i1);
 5494 
 5495       // step forward
 5496       i1 += i2;
 5497    }
 5498    setTextColor(-1);
 5499    if (bWithLF) {
 5500       // putchar('\n');
 5501       oprintf("\n"); // sfk1914
 5502    }
 5503 }
 5504 
 5505 char attribFromHumanColor(char *pszCol, char cDefault='i')
 5506 {
 5507    // red -> dark red. Red or RED -> bright red.
 5508    if (!mystricmp(pszCol, "red"))     return pszCol[0];
 5509    if (!mystricmp(pszCol, "green"))   return pszCol[0];
 5510    if (!mystricmp(pszCol, "blue"))    return pszCol[0];
 5511    if (!mystricmp(pszCol, "yellow"))  return pszCol[0];
 5512    if (!mystricmp(pszCol, "cyan"))    return pszCol[0];
 5513    if (!mystricmp(pszCol, "magenta")) return pszCol[0];
 5514    if (!mystricmp(pszCol, "def"))     return ' ';
 5515    if (!mystricmp(pszCol, "default")) return ' ';
 5516    if (!mystricmp(pszCol, "white"))   return (pszCol[0] == 'W') ? 'V':'v';
 5517 
 5518    // since 1.58 also supporting logical colors
 5519    if (!mystricmp(pszCol, "err"))     return 'e';